ts-video-player 0.1.0

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.
Files changed (251) hide show
  1. package/LICENSE.md +21 -0
  2. package/README.md +438 -0
  3. package/dist/cdn.d.ts +10 -0
  4. package/dist/cdn.d.ts.map +1 -0
  5. package/dist/cdn.min.js +1046 -0
  6. package/dist/cdn.min.js.map +54 -0
  7. package/dist/cdn.test.d.ts +2 -0
  8. package/dist/cdn.test.d.ts.map +1 -0
  9. package/dist/chunk-36x4yhjn.js +765 -0
  10. package/dist/chunk-36x4yhjn.js.map +12 -0
  11. package/dist/chunk-eajts2jv.js +4243 -0
  12. package/dist/chunk-eajts2jv.js.map +42 -0
  13. package/dist/chunk-z1ehbz9f.js +380 -0
  14. package/dist/chunk-z1ehbz9f.js.map +19 -0
  15. package/dist/config.d.ts +9 -0
  16. package/dist/config.d.ts.map +1 -0
  17. package/dist/core/audio-gain.d.ts +67 -0
  18. package/dist/core/audio-gain.d.ts.map +1 -0
  19. package/dist/core/caption-styles.d.ts +121 -0
  20. package/dist/core/caption-styles.d.ts.map +1 -0
  21. package/dist/core/chapters.d.ts +150 -0
  22. package/dist/core/chapters.d.ts.map +1 -0
  23. package/dist/core/error-overlay.d.ts +71 -0
  24. package/dist/core/error-overlay.d.ts.map +1 -0
  25. package/dist/core/events.d.ts +119 -0
  26. package/dist/core/events.d.ts.map +1 -0
  27. package/dist/core/features.d.ts +64 -0
  28. package/dist/core/features.d.ts.map +1 -0
  29. package/dist/core/i18n.d.ts +132 -0
  30. package/dist/core/i18n.d.ts.map +1 -0
  31. package/dist/core/index.d.ts +37 -0
  32. package/dist/core/index.d.ts.map +1 -0
  33. package/dist/core/live.d.ts +130 -0
  34. package/dist/core/live.d.ts.map +1 -0
  35. package/dist/core/media-session.d.ts +38 -0
  36. package/dist/core/media-session.d.ts.map +1 -0
  37. package/dist/core/orientation.d.ts +22 -0
  38. package/dist/core/orientation.d.ts.map +1 -0
  39. package/dist/core/popover.d.ts +42 -0
  40. package/dist/core/popover.d.ts.map +1 -0
  41. package/dist/core/remote-playback.d.ts +195 -0
  42. package/dist/core/remote-playback.d.ts.map +1 -0
  43. package/dist/core/settings-menu.d.ts +132 -0
  44. package/dist/core/settings-menu.d.ts.map +1 -0
  45. package/dist/core/spinner.d.ts +67 -0
  46. package/dist/core/spinner.d.ts.map +1 -0
  47. package/dist/core/state.d.ts +142 -0
  48. package/dist/core/state.d.ts.map +1 -0
  49. package/dist/core/text-tracks.d.ts +162 -0
  50. package/dist/core/text-tracks.d.ts.map +1 -0
  51. package/dist/core/themes.d.ts +213 -0
  52. package/dist/core/themes.d.ts.map +1 -0
  53. package/dist/core/thumbnails.d.ts +124 -0
  54. package/dist/core/thumbnails.d.ts.map +1 -0
  55. package/dist/core/tooltips.d.ts +100 -0
  56. package/dist/core/tooltips.d.ts.map +1 -0
  57. package/dist/elements/base.d.ts +3 -0
  58. package/dist/elements/base.d.ts.map +1 -0
  59. package/dist/elements/index.d.ts +33 -0
  60. package/dist/elements/index.d.ts.map +1 -0
  61. package/dist/elements/index.js +55 -0
  62. package/dist/elements/index.js.map +9 -0
  63. package/dist/elements/media-airplay-button.d.ts +13 -0
  64. package/dist/elements/media-airplay-button.d.ts.map +1 -0
  65. package/dist/elements/media-error-display.d.ts +13 -0
  66. package/dist/elements/media-error-display.d.ts.map +1 -0
  67. package/dist/elements/media-fullscreen-button.d.ts +13 -0
  68. package/dist/elements/media-fullscreen-button.d.ts.map +1 -0
  69. package/dist/elements/media-live-button.d.ts +13 -0
  70. package/dist/elements/media-live-button.d.ts.map +1 -0
  71. package/dist/elements/media-mute-button.d.ts +13 -0
  72. package/dist/elements/media-mute-button.d.ts.map +1 -0
  73. package/dist/elements/media-pip-button.d.ts +13 -0
  74. package/dist/elements/media-pip-button.d.ts.map +1 -0
  75. package/dist/elements/media-play-button.d.ts +14 -0
  76. package/dist/elements/media-play-button.d.ts.map +1 -0
  77. package/dist/elements/media-playback-rate-button.d.ts +10 -0
  78. package/dist/elements/media-playback-rate-button.d.ts.map +1 -0
  79. package/dist/elements/media-preview-time.d.ts +18 -0
  80. package/dist/elements/media-preview-time.d.ts.map +1 -0
  81. package/dist/elements/media-progress-bar.d.ts +16 -0
  82. package/dist/elements/media-progress-bar.d.ts.map +1 -0
  83. package/dist/elements/media-remote-playback-button.d.ts +16 -0
  84. package/dist/elements/media-remote-playback-button.d.ts.map +1 -0
  85. package/dist/elements/media-seek-button.d.ts +9 -0
  86. package/dist/elements/media-seek-button.d.ts.map +1 -0
  87. package/dist/elements/media-settings-menu.d.ts +32 -0
  88. package/dist/elements/media-settings-menu.d.ts.map +1 -0
  89. package/dist/elements/media-time-display.d.ts +21 -0
  90. package/dist/elements/media-time-display.d.ts.map +1 -0
  91. package/dist/elements/media-time-group.d.ts +22 -0
  92. package/dist/elements/media-time-group.d.ts.map +1 -0
  93. package/dist/elements/media-time-separator.d.ts +13 -0
  94. package/dist/elements/media-time-separator.d.ts.map +1 -0
  95. package/dist/elements/media-volume-slider.d.ts +16 -0
  96. package/dist/elements/media-volume-slider.d.ts.map +1 -0
  97. package/dist/elements/utils.d.ts +27 -0
  98. package/dist/elements/utils.d.ts.map +1 -0
  99. package/dist/elements/video-player.d.ts +23 -0
  100. package/dist/elements/video-player.d.ts.map +1 -0
  101. package/dist/elements/video-skin.d.ts +23 -0
  102. package/dist/elements/video-skin.d.ts.map +1 -0
  103. package/dist/features/captions.d.ts +8 -0
  104. package/dist/features/captions.d.ts.map +1 -0
  105. package/dist/features/create-player.d.ts +45 -0
  106. package/dist/features/create-player.d.ts.map +1 -0
  107. package/dist/features/fullscreen.d.ts +8 -0
  108. package/dist/features/fullscreen.d.ts.map +1 -0
  109. package/dist/features/gestures.d.ts +8 -0
  110. package/dist/features/gestures.d.ts.map +1 -0
  111. package/dist/features/index.d.ts +26 -0
  112. package/dist/features/index.d.ts.map +1 -0
  113. package/dist/features/index.js +31 -0
  114. package/dist/features/index.js.map +9 -0
  115. package/dist/features/keyboard.d.ts +8 -0
  116. package/dist/features/keyboard.d.ts.map +1 -0
  117. package/dist/features/pip.d.ts +8 -0
  118. package/dist/features/pip.d.ts.map +1 -0
  119. package/dist/features/playback.d.ts +8 -0
  120. package/dist/features/playback.d.ts.map +1 -0
  121. package/dist/features/quality.d.ts +8 -0
  122. package/dist/features/quality.d.ts.map +1 -0
  123. package/dist/features/types.d.ts +26 -0
  124. package/dist/features/types.d.ts.map +1 -0
  125. package/dist/features/volume.d.ts +8 -0
  126. package/dist/features/volume.d.ts.map +1 -0
  127. package/dist/index.d.ts +84 -0
  128. package/dist/index.d.ts.map +1 -0
  129. package/dist/index.js +8149 -0
  130. package/dist/index.js.map +31 -0
  131. package/dist/layouts/index.d.ts +58 -0
  132. package/dist/layouts/index.d.ts.map +1 -0
  133. package/dist/player.d.ts +90 -0
  134. package/dist/player.d.ts.map +1 -0
  135. package/dist/plugins/ads.d.ts +311 -0
  136. package/dist/plugins/ads.d.ts.map +1 -0
  137. package/dist/plugins/analytics.d.ts +203 -0
  138. package/dist/plugins/analytics.d.ts.map +1 -0
  139. package/dist/plugins/end-screen.d.ts +184 -0
  140. package/dist/plugins/end-screen.d.ts.map +1 -0
  141. package/dist/plugins/index.d.ts +13 -0
  142. package/dist/plugins/index.d.ts.map +1 -0
  143. package/dist/plugins/index.js +20 -0
  144. package/dist/plugins/index.js.map +9 -0
  145. package/dist/plugins/skip-segments.d.ts +159 -0
  146. package/dist/plugins/skip-segments.d.ts.map +1 -0
  147. package/dist/plugins/watermark.d.ts +157 -0
  148. package/dist/plugins/watermark.d.ts.map +1 -0
  149. package/dist/providers/base.d.ts +56 -0
  150. package/dist/providers/base.d.ts.map +1 -0
  151. package/dist/providers/dash.d.ts +94 -0
  152. package/dist/providers/dash.d.ts.map +1 -0
  153. package/dist/providers/hls.d.ts +182 -0
  154. package/dist/providers/hls.d.ts.map +1 -0
  155. package/dist/providers/html5.d.ts +64 -0
  156. package/dist/providers/html5.d.ts.map +1 -0
  157. package/dist/providers/index.d.ts +41 -0
  158. package/dist/providers/index.d.ts.map +1 -0
  159. package/dist/providers/vimeo.d.ts +157 -0
  160. package/dist/providers/vimeo.d.ts.map +1 -0
  161. package/dist/providers/youtube.d.ts +134 -0
  162. package/dist/providers/youtube.d.ts.map +1 -0
  163. package/dist/stx/index.d.ts +59 -0
  164. package/dist/stx/index.d.ts.map +1 -0
  165. package/dist/stx/index.js +480 -0
  166. package/dist/stx/index.js.map +11 -0
  167. package/dist/types.d.ts +724 -0
  168. package/dist/types.d.ts.map +1 -0
  169. package/dist/ui/components.d.ts +121 -0
  170. package/dist/ui/components.d.ts.map +1 -0
  171. package/dist/ui/index.d.ts +9 -0
  172. package/dist/ui/index.d.ts.map +1 -0
  173. package/dist/ui/index.js +16 -0
  174. package/dist/ui/index.js.map +9 -0
  175. package/package.json +109 -0
  176. package/src/cdn.test.ts +10 -0
  177. package/src/cdn.ts +60 -0
  178. package/src/config.ts +25 -0
  179. package/src/core/audio-gain.ts +171 -0
  180. package/src/core/caption-styles.ts +568 -0
  181. package/src/core/chapters.ts +551 -0
  182. package/src/core/error-overlay.ts +354 -0
  183. package/src/core/events.ts +552 -0
  184. package/src/core/features.ts +287 -0
  185. package/src/core/i18n.ts +590 -0
  186. package/src/core/index.ts +249 -0
  187. package/src/core/live.ts +472 -0
  188. package/src/core/media-session.ts +137 -0
  189. package/src/core/orientation.ts +57 -0
  190. package/src/core/popover.ts +143 -0
  191. package/src/core/remote-playback.ts +661 -0
  192. package/src/core/settings-menu.ts +630 -0
  193. package/src/core/spinner.ts +310 -0
  194. package/src/core/state.ts +476 -0
  195. package/src/core/text-tracks.ts +599 -0
  196. package/src/core/themes.ts +607 -0
  197. package/src/core/thumbnails.ts +457 -0
  198. package/src/core/tooltips.ts +370 -0
  199. package/src/elements/base.ts +2 -0
  200. package/src/elements/index.ts +82 -0
  201. package/src/elements/media-airplay-button.ts +78 -0
  202. package/src/elements/media-error-display.ts +47 -0
  203. package/src/elements/media-fullscreen-button.ts +72 -0
  204. package/src/elements/media-live-button.ts +65 -0
  205. package/src/elements/media-mute-button.ts +69 -0
  206. package/src/elements/media-pip-button.ts +66 -0
  207. package/src/elements/media-play-button.ts +69 -0
  208. package/src/elements/media-playback-rate-button.ts +59 -0
  209. package/src/elements/media-preview-time.ts +75 -0
  210. package/src/elements/media-progress-bar.ts +200 -0
  211. package/src/elements/media-remote-playback-button.ts +108 -0
  212. package/src/elements/media-seek-button.ts +50 -0
  213. package/src/elements/media-settings-menu.ts +302 -0
  214. package/src/elements/media-time-display.ts +99 -0
  215. package/src/elements/media-time-group.ts +41 -0
  216. package/src/elements/media-time-separator.ts +35 -0
  217. package/src/elements/media-volume-slider.ts +140 -0
  218. package/src/elements/utils.ts +82 -0
  219. package/src/elements/video-player.ts +175 -0
  220. package/src/elements/video-skin.ts +127 -0
  221. package/src/features/captions.ts +56 -0
  222. package/src/features/create-player.ts +91 -0
  223. package/src/features/fullscreen.ts +44 -0
  224. package/src/features/gestures.ts +44 -0
  225. package/src/features/index.ts +44 -0
  226. package/src/features/keyboard.ts +89 -0
  227. package/src/features/pip.ts +47 -0
  228. package/src/features/playback.ts +68 -0
  229. package/src/features/quality.ts +19 -0
  230. package/src/features/types.ts +28 -0
  231. package/src/features/volume.ts +46 -0
  232. package/src/index.ts +635 -0
  233. package/src/layouts/index.ts +672 -0
  234. package/src/player.ts +821 -0
  235. package/src/plugins/ads.ts +1045 -0
  236. package/src/plugins/analytics.ts +673 -0
  237. package/src/plugins/end-screen.ts +793 -0
  238. package/src/plugins/index.ts +61 -0
  239. package/src/plugins/skip-segments.ts +537 -0
  240. package/src/plugins/watermark.ts +537 -0
  241. package/src/providers/base.ts +197 -0
  242. package/src/providers/dash.ts +588 -0
  243. package/src/providers/hls.ts +624 -0
  244. package/src/providers/html5.ts +473 -0
  245. package/src/providers/index.ts +97 -0
  246. package/src/providers/vimeo.ts +533 -0
  247. package/src/providers/youtube.ts +542 -0
  248. package/src/stx/index.ts +606 -0
  249. package/src/types.ts +874 -0
  250. package/src/ui/components.ts +910 -0
  251. package/src/ui/index.ts +29 -0
@@ -0,0 +1,590 @@
1
+ /**
2
+ * Internationalization (i18n) System
3
+ *
4
+ * Provides translation support for all player UI strings.
5
+ *
6
+ * @module core/i18n
7
+ */
8
+
9
+ // =============================================================================
10
+ // Types
11
+ // =============================================================================
12
+
13
+ export interface TranslationStrings {
14
+ // Playback
15
+ play: string
16
+ pause: string
17
+ replay: string
18
+ // Volume
19
+ mute: string
20
+ unmute: string
21
+ volume: string
22
+ // Progress
23
+ seekBackward: string
24
+ seekForward: string
25
+ seekTo: string
26
+ duration: string
27
+ currentTime: string
28
+ played: string
29
+ buffered: string
30
+ // Fullscreen
31
+ enterFullscreen: string
32
+ exitFullscreen: string
33
+ // PiP
34
+ enterPiP: string
35
+ exitPiP: string
36
+ // Captions
37
+ enableCaptions: string
38
+ disableCaptions: string
39
+ captions: string
40
+ captionsOff: string
41
+ // Settings
42
+ settings: string
43
+ quality: string
44
+ auto: string
45
+ speed: string
46
+ normal: string
47
+ loop: string
48
+ loopEnabled: string
49
+ loopDisabled: string
50
+ // Audio
51
+ audioBoost: string
52
+ audioTrack: string
53
+ // Live
54
+ live: string
55
+ liveEdge: string
56
+ seekToLive: string
57
+ behindLive: string
58
+ // Errors
59
+ networkError: string
60
+ mediaError: string
61
+ unknownError: string
62
+ // Loading
63
+ loading: string
64
+ buffering: string
65
+ // Chapters
66
+ chapters: string
67
+ // AirPlay / Cast
68
+ airPlay: string
69
+ googleCast: string
70
+ casting: string
71
+ // Misc
72
+ download: string
73
+ share: string
74
+ close: string
75
+ menu: string
76
+ back: string
77
+ }
78
+
79
+ export type TranslationKey = keyof TranslationStrings
80
+
81
+ export interface I18nConfig {
82
+ /** Current language code */
83
+ language?: string
84
+ /** Fallback language code */
85
+ fallbackLanguage?: string
86
+ /** Custom translations */
87
+ translations?: Partial<Record<string, Partial<TranslationStrings>>>
88
+ /** Interpolation pattern (default: {key}) */
89
+ interpolationPattern?: RegExp
90
+ }
91
+
92
+ // =============================================================================
93
+ // Default Translations
94
+ // =============================================================================
95
+
96
+ export const englishStrings: TranslationStrings = {
97
+ // Playback
98
+ play: 'Play',
99
+ pause: 'Pause',
100
+ replay: 'Replay',
101
+ // Volume
102
+ mute: 'Mute',
103
+ unmute: 'Unmute',
104
+ volume: 'Volume',
105
+ // Progress
106
+ seekBackward: 'Seek backward {seconds} seconds',
107
+ seekForward: 'Seek forward {seconds} seconds',
108
+ seekTo: 'Seek to {time}',
109
+ duration: 'Duration',
110
+ currentTime: 'Current time',
111
+ played: 'Played',
112
+ buffered: 'Buffered',
113
+ // Fullscreen
114
+ enterFullscreen: 'Enter fullscreen',
115
+ exitFullscreen: 'Exit fullscreen',
116
+ // PiP
117
+ enterPiP: 'Enter picture-in-picture',
118
+ exitPiP: 'Exit picture-in-picture',
119
+ // Captions
120
+ enableCaptions: 'Enable captions',
121
+ disableCaptions: 'Disable captions',
122
+ captions: 'Captions',
123
+ captionsOff: 'Off',
124
+ // Settings
125
+ settings: 'Settings',
126
+ quality: 'Quality',
127
+ auto: 'Auto',
128
+ speed: 'Speed',
129
+ normal: 'Normal',
130
+ loop: 'Loop',
131
+ loopEnabled: 'Loop enabled',
132
+ loopDisabled: 'Loop disabled',
133
+ // Audio
134
+ audioBoost: 'Audio boost',
135
+ audioTrack: 'Audio track',
136
+ // Live
137
+ live: 'LIVE',
138
+ liveEdge: 'At live edge',
139
+ seekToLive: 'Seek to live edge',
140
+ behindLive: '{seconds}s behind',
141
+ // Errors
142
+ networkError: 'A network error occurred',
143
+ mediaError: 'The media could not be loaded',
144
+ unknownError: 'An unknown error occurred',
145
+ // Loading
146
+ loading: 'Loading',
147
+ buffering: 'Buffering',
148
+ // Chapters
149
+ chapters: 'Chapters',
150
+ // AirPlay / Cast
151
+ airPlay: 'AirPlay',
152
+ googleCast: 'Cast',
153
+ casting: 'Casting to {device}',
154
+ // Misc
155
+ download: 'Download',
156
+ share: 'Share',
157
+ close: 'Close',
158
+ menu: 'Menu',
159
+ back: 'Back',
160
+ }
161
+
162
+ export const spanishStrings: TranslationStrings = {
163
+ play: 'Reproducir',
164
+ pause: 'Pausar',
165
+ replay: 'Repetir',
166
+ mute: 'Silenciar',
167
+ unmute: 'Activar sonido',
168
+ volume: 'Volumen',
169
+ seekBackward: 'Retroceder {seconds} segundos',
170
+ seekForward: 'Avanzar {seconds} segundos',
171
+ seekTo: 'Ir a {time}',
172
+ duration: 'Duración',
173
+ currentTime: 'Tiempo actual',
174
+ played: 'Reproducido',
175
+ buffered: 'Almacenado',
176
+ enterFullscreen: 'Pantalla completa',
177
+ exitFullscreen: 'Salir de pantalla completa',
178
+ enterPiP: 'Imagen en imagen',
179
+ exitPiP: 'Salir de imagen en imagen',
180
+ enableCaptions: 'Activar subtítulos',
181
+ disableCaptions: 'Desactivar subtítulos',
182
+ captions: 'Subtítulos',
183
+ captionsOff: 'Desactivados',
184
+ settings: 'Ajustes',
185
+ quality: 'Calidad',
186
+ auto: 'Automático',
187
+ speed: 'Velocidad',
188
+ normal: 'Normal',
189
+ loop: 'Bucle',
190
+ loopEnabled: 'Bucle activado',
191
+ loopDisabled: 'Bucle desactivado',
192
+ audioBoost: 'Aumento de audio',
193
+ audioTrack: 'Pista de audio',
194
+ live: 'EN VIVO',
195
+ liveEdge: 'En vivo',
196
+ seekToLive: 'Ir al directo',
197
+ behindLive: '{seconds}s de retraso',
198
+ networkError: 'Error de red',
199
+ mediaError: 'No se pudo cargar el medio',
200
+ unknownError: 'Error desconocido',
201
+ loading: 'Cargando',
202
+ buffering: 'Almacenando',
203
+ chapters: 'Capítulos',
204
+ airPlay: 'AirPlay',
205
+ googleCast: 'Enviar',
206
+ casting: 'Enviando a {device}',
207
+ download: 'Descargar',
208
+ share: 'Compartir',
209
+ close: 'Cerrar',
210
+ menu: 'Menú',
211
+ back: 'Volver',
212
+ }
213
+
214
+ export const frenchStrings: TranslationStrings = {
215
+ play: 'Lecture',
216
+ pause: 'Pause',
217
+ replay: 'Relire',
218
+ mute: 'Muet',
219
+ unmute: 'Son activé',
220
+ volume: 'Volume',
221
+ seekBackward: 'Reculer de {seconds} secondes',
222
+ seekForward: 'Avancer de {seconds} secondes',
223
+ seekTo: 'Aller à {time}',
224
+ duration: 'Durée',
225
+ currentTime: 'Temps actuel',
226
+ played: 'Joué',
227
+ buffered: 'Mis en mémoire tampon',
228
+ enterFullscreen: 'Plein écran',
229
+ exitFullscreen: 'Quitter le plein écran',
230
+ enterPiP: 'Image dans l\'image',
231
+ exitPiP: 'Quitter l\'image dans l\'image',
232
+ enableCaptions: 'Activer les sous-titres',
233
+ disableCaptions: 'Désactiver les sous-titres',
234
+ captions: 'Sous-titres',
235
+ captionsOff: 'Désactivés',
236
+ settings: 'Paramètres',
237
+ quality: 'Qualité',
238
+ auto: 'Auto',
239
+ speed: 'Vitesse',
240
+ normal: 'Normal',
241
+ loop: 'Boucle',
242
+ loopEnabled: 'Boucle activée',
243
+ loopDisabled: 'Boucle désactivée',
244
+ audioBoost: 'Amplification audio',
245
+ audioTrack: 'Piste audio',
246
+ live: 'EN DIRECT',
247
+ liveEdge: 'En direct',
248
+ seekToLive: 'Aller au direct',
249
+ behindLive: '{seconds}s de retard',
250
+ networkError: 'Erreur réseau',
251
+ mediaError: 'Le média n\'a pas pu être chargé',
252
+ unknownError: 'Erreur inconnue',
253
+ loading: 'Chargement',
254
+ buffering: 'Mise en mémoire tampon',
255
+ chapters: 'Chapitres',
256
+ airPlay: 'AirPlay',
257
+ googleCast: 'Caster',
258
+ casting: 'Diffusion sur {device}',
259
+ download: 'Télécharger',
260
+ share: 'Partager',
261
+ close: 'Fermer',
262
+ menu: 'Menu',
263
+ back: 'Retour',
264
+ }
265
+
266
+ export const germanStrings: TranslationStrings = {
267
+ play: 'Abspielen',
268
+ pause: 'Pause',
269
+ replay: 'Wiederholen',
270
+ mute: 'Stumm',
271
+ unmute: 'Ton an',
272
+ volume: 'Lautstärke',
273
+ seekBackward: '{seconds} Sekunden zurück',
274
+ seekForward: '{seconds} Sekunden vor',
275
+ seekTo: 'Zu {time} springen',
276
+ duration: 'Dauer',
277
+ currentTime: 'Aktuelle Zeit',
278
+ played: 'Abgespielt',
279
+ buffered: 'Gepuffert',
280
+ enterFullscreen: 'Vollbild',
281
+ exitFullscreen: 'Vollbild beenden',
282
+ enterPiP: 'Bild-in-Bild',
283
+ exitPiP: 'Bild-in-Bild beenden',
284
+ enableCaptions: 'Untertitel aktivieren',
285
+ disableCaptions: 'Untertitel deaktivieren',
286
+ captions: 'Untertitel',
287
+ captionsOff: 'Aus',
288
+ settings: 'Einstellungen',
289
+ quality: 'Qualität',
290
+ auto: 'Auto',
291
+ speed: 'Geschwindigkeit',
292
+ normal: 'Normal',
293
+ loop: 'Schleife',
294
+ loopEnabled: 'Schleife aktiviert',
295
+ loopDisabled: 'Schleife deaktiviert',
296
+ audioBoost: 'Audio-Verstärkung',
297
+ audioTrack: 'Audiospur',
298
+ live: 'LIVE',
299
+ liveEdge: 'Am Live-Punkt',
300
+ seekToLive: 'Zum Live-Punkt',
301
+ behindLive: '{seconds}s hinter Live',
302
+ networkError: 'Netzwerkfehler',
303
+ mediaError: 'Medium konnte nicht geladen werden',
304
+ unknownError: 'Unbekannter Fehler',
305
+ loading: 'Wird geladen',
306
+ buffering: 'Puffern',
307
+ chapters: 'Kapitel',
308
+ airPlay: 'AirPlay',
309
+ googleCast: 'Streamen',
310
+ casting: 'Streame zu {device}',
311
+ download: 'Herunterladen',
312
+ share: 'Teilen',
313
+ close: 'Schließen',
314
+ menu: 'Menü',
315
+ back: 'Zurück',
316
+ }
317
+
318
+ export const japaneseStrings: TranslationStrings = {
319
+ play: '再生',
320
+ pause: '一時停止',
321
+ replay: 'リプレイ',
322
+ mute: 'ミュート',
323
+ unmute: 'ミュート解除',
324
+ volume: '音量',
325
+ seekBackward: '{seconds}秒戻る',
326
+ seekForward: '{seconds}秒進む',
327
+ seekTo: '{time}にシーク',
328
+ duration: '長さ',
329
+ currentTime: '現在の時間',
330
+ played: '再生済み',
331
+ buffered: 'バッファ済み',
332
+ enterFullscreen: 'フルスクリーン',
333
+ exitFullscreen: 'フルスクリーン終了',
334
+ enterPiP: 'ピクチャーインピクチャー',
335
+ exitPiP: 'ピクチャーインピクチャー終了',
336
+ enableCaptions: '字幕を有効化',
337
+ disableCaptions: '字幕を無効化',
338
+ captions: '字幕',
339
+ captionsOff: 'オフ',
340
+ settings: '設定',
341
+ quality: '画質',
342
+ auto: '自動',
343
+ speed: '再生速度',
344
+ normal: '通常',
345
+ loop: 'ループ',
346
+ loopEnabled: 'ループ有効',
347
+ loopDisabled: 'ループ無効',
348
+ audioBoost: 'オーディオブースト',
349
+ audioTrack: 'オーディオトラック',
350
+ live: 'ライブ',
351
+ liveEdge: 'ライブ中',
352
+ seekToLive: 'ライブに移動',
353
+ behindLive: '{seconds}秒遅れ',
354
+ networkError: 'ネットワークエラー',
355
+ mediaError: 'メディアを読み込めません',
356
+ unknownError: '不明なエラー',
357
+ loading: '読み込み中',
358
+ buffering: 'バッファリング',
359
+ chapters: 'チャプター',
360
+ airPlay: 'AirPlay',
361
+ googleCast: 'キャスト',
362
+ casting: '{device}にキャスト中',
363
+ download: 'ダウンロード',
364
+ share: '共有',
365
+ close: '閉じる',
366
+ menu: 'メニュー',
367
+ back: '戻る',
368
+ }
369
+
370
+ export const chineseStrings: TranslationStrings = {
371
+ play: '播放',
372
+ pause: '暂停',
373
+ replay: '重播',
374
+ mute: '静音',
375
+ unmute: '取消静音',
376
+ volume: '音量',
377
+ seekBackward: '后退{seconds}秒',
378
+ seekForward: '前进{seconds}秒',
379
+ seekTo: '跳转到{time}',
380
+ duration: '时长',
381
+ currentTime: '当前时间',
382
+ played: '已播放',
383
+ buffered: '已缓冲',
384
+ enterFullscreen: '全屏',
385
+ exitFullscreen: '退出全屏',
386
+ enterPiP: '画中画',
387
+ exitPiP: '退出画中画',
388
+ enableCaptions: '开启字幕',
389
+ disableCaptions: '关闭字幕',
390
+ captions: '字幕',
391
+ captionsOff: '关闭',
392
+ settings: '设置',
393
+ quality: '画质',
394
+ auto: '自动',
395
+ speed: '播放速度',
396
+ normal: '正常',
397
+ loop: '循环',
398
+ loopEnabled: '循环已开启',
399
+ loopDisabled: '循环已关闭',
400
+ audioBoost: '音频增强',
401
+ audioTrack: '音轨',
402
+ live: '直播',
403
+ liveEdge: '直播中',
404
+ seekToLive: '跳转到直播',
405
+ behindLive: '落后{seconds}秒',
406
+ networkError: '网络错误',
407
+ mediaError: '无法加载媒体',
408
+ unknownError: '未知错误',
409
+ loading: '加载中',
410
+ buffering: '缓冲中',
411
+ chapters: '章节',
412
+ airPlay: 'AirPlay',
413
+ googleCast: '投射',
414
+ casting: '正在投射到{device}',
415
+ download: '下载',
416
+ share: '分享',
417
+ close: '关闭',
418
+ menu: '菜单',
419
+ back: '返回',
420
+ }
421
+
422
+ export const defaultTranslations: Record<string, TranslationStrings> = {
423
+ en: englishStrings,
424
+ es: spanishStrings,
425
+ fr: frenchStrings,
426
+ de: germanStrings,
427
+ ja: japaneseStrings,
428
+ zh: chineseStrings,
429
+ }
430
+
431
+ // =============================================================================
432
+ // I18n Class
433
+ // =============================================================================
434
+
435
+ export class I18n {
436
+ private language: string
437
+ private fallbackLanguage: string
438
+ private translations: Record<string, Partial<TranslationStrings>>
439
+ private interpolationPattern: RegExp
440
+
441
+ constructor(config: I18nConfig = {}) {
442
+ this.language = config.language || this.detectLanguage() || 'en'
443
+ this.fallbackLanguage = config.fallbackLanguage || 'en'
444
+ this.translations = { ...defaultTranslations }
445
+
446
+ // Merge custom translations
447
+ if (config.translations) {
448
+ for (const [lang, strings] of Object.entries(config.translations)) {
449
+ if (strings) {
450
+ this.translations[lang] = { ...this.translations[lang], ...strings }
451
+ }
452
+ }
453
+ }
454
+
455
+ this.interpolationPattern = config.interpolationPattern || /\{(\w+)\}/g
456
+ }
457
+
458
+ /**
459
+ * Detect browser language
460
+ */
461
+ private detectLanguage(): string | null {
462
+ if (typeof navigator === 'undefined') return null
463
+
464
+ const lang = navigator.language || (navigator as any).userLanguage
465
+ if (!lang) return null
466
+
467
+ // Try exact match first
468
+ if (this.translations[lang]) {
469
+ return lang
470
+ }
471
+
472
+ // Try base language (e.g., 'en' from 'en-US')
473
+ const baseLang = lang.split('-')[0]
474
+ if (this.translations[baseLang]) {
475
+ return baseLang
476
+ }
477
+
478
+ return null
479
+ }
480
+
481
+ /**
482
+ * Get current language
483
+ */
484
+ getLanguage(): string {
485
+ return this.language
486
+ }
487
+
488
+ /**
489
+ * Set current language
490
+ */
491
+ setLanguage(language: string): void {
492
+ this.language = language
493
+ }
494
+
495
+ /**
496
+ * Get translation for a key
497
+ */
498
+ t(key: TranslationKey, params?: Record<string, string | number>): string {
499
+ // Try current language
500
+ let str = this.translations[this.language]?.[key]
501
+
502
+ // Fall back to fallback language
503
+ if (!str) {
504
+ str = this.translations[this.fallbackLanguage]?.[key]
505
+ }
506
+
507
+ // Return key if no translation found
508
+ if (!str) {
509
+ return key
510
+ }
511
+
512
+ // Interpolate params
513
+ if (params) {
514
+ str = str.replace(this.interpolationPattern, (match, paramKey) => {
515
+ const value = params[paramKey]
516
+ return value !== undefined ? String(value) : match
517
+ })
518
+ }
519
+
520
+ return str
521
+ }
522
+
523
+ /**
524
+ * Add or update translations for a language
525
+ */
526
+ addTranslations(language: string, strings: Partial<TranslationStrings>): void {
527
+ this.translations[language] = {
528
+ ...this.translations[language],
529
+ ...strings,
530
+ }
531
+ }
532
+
533
+ /**
534
+ * Get all translations for a language
535
+ */
536
+ getTranslations(language: string): Partial<TranslationStrings> | undefined {
537
+ return this.translations[language]
538
+ }
539
+
540
+ /**
541
+ * Get all available languages
542
+ */
543
+ getAvailableLanguages(): string[] {
544
+ return Object.keys(this.translations)
545
+ }
546
+
547
+ /**
548
+ * Check if a language is available
549
+ */
550
+ hasLanguage(language: string): boolean {
551
+ return language in this.translations
552
+ }
553
+ }
554
+
555
+ // =============================================================================
556
+ // Factory
557
+ // =============================================================================
558
+
559
+ let globalI18n: I18n | null = null
560
+
561
+ /**
562
+ * Create an I18n instance
563
+ */
564
+ export function createI18n(config?: I18nConfig): I18n {
565
+ return new I18n(config)
566
+ }
567
+
568
+ /**
569
+ * Get or create global I18n instance
570
+ */
571
+ export function getI18n(config?: I18nConfig): I18n {
572
+ if (!globalI18n) {
573
+ globalI18n = createI18n(config)
574
+ }
575
+ return globalI18n
576
+ }
577
+
578
+ /**
579
+ * Set global I18n instance
580
+ */
581
+ export function setI18n(i18n: I18n): void {
582
+ globalI18n = i18n
583
+ }
584
+
585
+ /**
586
+ * Quick translation helper using global instance
587
+ */
588
+ export function t(key: TranslationKey, params?: Record<string, string | number>): string {
589
+ return getI18n().t(key, params)
590
+ }