aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--client/src/app/videos/+video-watch/video-watch.component.ts19
-rw-r--r--client/src/assets/player/peertube-link-button.ts2
-rw-r--r--client/src/assets/player/peertube-player.ts67
-rw-r--r--client/src/assets/player/peertube-videojs-plugin.ts10
-rw-r--r--client/src/assets/player/resolution-menu-button.ts7
-rw-r--r--client/src/assets/player/settings-menu-button.ts2
-rw-r--r--client/src/assets/player/settings-menu-item.ts4
-rw-r--r--client/src/assets/player/utils.ts2
-rw-r--r--client/src/assets/player/webtorrent-info-button.ts7
-rw-r--r--client/src/locale/source/player_en_US.xml378
-rw-r--r--client/src/locale/source/videojs_en_US.json85
-rw-r--r--client/src/locale/target/player_fr.json1
-rw-r--r--client/src/locale/target/player_fr.xml379
-rw-r--r--client/src/standalone/videos/embed.ts12
-rw-r--r--package.json3
-rwxr-xr-xscripts/i18n/create-custom-files.ts49
-rwxr-xr-xscripts/i18n/generate.sh6
-rwxr-xr-xscripts/i18n/pull-hook.sh6
-rwxr-xr-xscripts/i18n/xliff2json.ts42
-rwxr-xr-xscripts/watch/server.sh5
-rw-r--r--server/controllers/client.ts8
-rw-r--r--shared/models/i18n/i18n.ts4
-rw-r--r--yarn.lock12
23 files changed, 1049 insertions, 61 deletions
diff --git a/client/src/app/videos/+video-watch/video-watch.component.ts b/client/src/app/videos/+video-watch/video-watch.component.ts
index 23d74494c..d3e16c4cf 100644
--- a/client/src/app/videos/+video-watch/video-watch.component.ts
+++ b/client/src/app/videos/+video-watch/video-watch.component.ts
@@ -1,5 +1,5 @@
1import { catchError } from 'rxjs/operators' 1import { catchError } from 'rxjs/operators'
2import { Component, ElementRef, NgZone, OnDestroy, OnInit, ViewChild } from '@angular/core' 2import { Component, ElementRef, LOCALE_ID, NgZone, OnDestroy, OnInit, ViewChild, Inject } from '@angular/core'
3import { ActivatedRoute, Router } from '@angular/router' 3import { ActivatedRoute, Router } from '@angular/router'
4import { RedirectService } from '@app/core/routing/redirect.service' 4import { RedirectService } from '@app/core/routing/redirect.service'
5import { peertubeLocalStorage } from '@app/shared/misc/peertube-local-storage' 5import { peertubeLocalStorage } from '@app/shared/misc/peertube-local-storage'
@@ -21,9 +21,10 @@ import { MarkdownService } from '../shared'
21import { VideoDownloadComponent } from './modal/video-download.component' 21import { VideoDownloadComponent } from './modal/video-download.component'
22import { VideoReportComponent } from './modal/video-report.component' 22import { VideoReportComponent } from './modal/video-report.component'
23import { VideoShareComponent } from './modal/video-share.component' 23import { VideoShareComponent } from './modal/video-share.component'
24import { getVideojsOptions } from '../../../assets/player/peertube-player' 24import { getVideojsOptions, loadLocale, addContextMenu } from '../../../assets/player/peertube-player'
25import { ServerService } from '@app/core' 25import { ServerService } from '@app/core'
26import { I18n } from '@ngx-translate/i18n-polyfill' 26import { I18n } from '@ngx-translate/i18n-polyfill'
27import { environment } from '../../../environments/environment'
27 28
28@Component({ 29@Component({
29 selector: 'my-video-watch', 30 selector: 'my-video-watch',
@@ -54,6 +55,7 @@ export class VideoWatchComponent implements OnInit, OnDestroy {
54 likesBarTooltipText = '' 55 likesBarTooltipText = ''
55 hasAlreadyAcceptedPrivacyConcern = false 56 hasAlreadyAcceptedPrivacyConcern = false
56 57
58 private videojsLocaleLoaded = false
57 private otherVideos: Video[] = [] 59 private otherVideos: Video[] = []
58 private paramsSub: Subscription 60 private paramsSub: Subscription
59 61
@@ -72,7 +74,8 @@ export class VideoWatchComponent implements OnInit, OnDestroy {
72 private markdownService: MarkdownService, 74 private markdownService: MarkdownService,
73 private zone: NgZone, 75 private zone: NgZone,
74 private redirectService: RedirectService, 76 private redirectService: RedirectService,
75 private i18n: I18n 77 private i18n: I18n,
78 @Inject(LOCALE_ID) private localeId: string
76 ) {} 79 ) {}
77 80
78 get user () { 81 get user () {
@@ -365,7 +368,6 @@ export class VideoWatchComponent implements OnInit, OnDestroy {
365 inactivityTimeout: 2500, 368 inactivityTimeout: 2500,
366 videoFiles: this.video.files, 369 videoFiles: this.video.files,
367 playerElement: this.playerElement, 370 playerElement: this.playerElement,
368 videoEmbedUrl: this.video.embedUrl,
369 videoViewUrl: this.videoService.getVideoViewUrl(this.video.uuid), 371 videoViewUrl: this.videoService.getVideoViewUrl(this.video.uuid),
370 videoDuration: this.video.duration, 372 videoDuration: this.video.duration,
371 enableHotkeys: true, 373 enableHotkeys: true,
@@ -374,11 +376,18 @@ export class VideoWatchComponent implements OnInit, OnDestroy {
374 startTime 376 startTime
375 }) 377 })
376 378
379 if (this.videojsLocaleLoaded === false) {
380 await loadLocale(environment.apiUrl, videojs, environment.production === true ? this.localeId : 'fr')
381 this.videojsLocaleLoaded = true
382 }
383
377 const self = this 384 const self = this
378 this.zone.runOutsideAngular(() => { 385 this.zone.runOutsideAngular(async () => {
379 videojs(this.playerElement, videojsOptions, function () { 386 videojs(this.playerElement, videojsOptions, function () {
380 self.player = this 387 self.player = this
381 this.on('customError', (event, data) => self.handleError(data.err)) 388 this.on('customError', (event, data) => self.handleError(data.err))
389
390 addContextMenu(self.player, self.video.embedUrl)
382 }) 391 })
383 }) 392 })
384 393
diff --git a/client/src/assets/player/peertube-link-button.ts b/client/src/assets/player/peertube-link-button.ts
index a13815d61..26f8b9d73 100644
--- a/client/src/assets/player/peertube-link-button.ts
+++ b/client/src/assets/player/peertube-link-button.ts
@@ -24,7 +24,7 @@ class PeerTubeLinkButton extends Button {
24 const el = videojsUntyped.dom.createEl('a', { 24 const el = videojsUntyped.dom.createEl('a', {
25 href: buildVideoLink(), 25 href: buildVideoLink(),
26 innerHTML: 'PeerTube', 26 innerHTML: 'PeerTube',
27 title: 'Go to the video page', 27 title: this.player_.localize('Go to the video page'),
28 className: 'vjs-peertube-link', 28 className: 'vjs-peertube-link',
29 target: '_blank' 29 target: '_blank'
30 }) 30 })
diff --git a/client/src/assets/player/peertube-player.ts b/client/src/assets/player/peertube-player.ts
index d204b9703..b604097fa 100644
--- a/client/src/assets/player/peertube-player.ts
+++ b/client/src/assets/player/peertube-player.ts
@@ -12,6 +12,7 @@ import './peertube-videojs-plugin'
12import './peertube-load-progress-bar' 12import './peertube-load-progress-bar'
13import { videojsUntyped } from './peertube-videojs-typings' 13import { videojsUntyped } from './peertube-videojs-typings'
14import { buildVideoEmbed, buildVideoLink, copyToClipboard } from './utils' 14import { buildVideoEmbed, buildVideoLink, copyToClipboard } from './utils'
15import { is18nLocale, isDefaultLocale } from '../../../../shared/models/i18n/i18n'
15 16
16// Change 'Playback Rate' to 'Speed' (smaller for our settings menu) 17// Change 'Playback Rate' to 'Speed' (smaller for our settings menu)
17videojsUntyped.getComponent('PlaybackRateMenuButton').prototype.controlText_ = 'Speed' 18videojsUntyped.getComponent('PlaybackRateMenuButton').prototype.controlText_ = 'Speed'
@@ -20,7 +21,6 @@ function getVideojsOptions (options: {
20 autoplay: boolean, 21 autoplay: boolean,
21 playerElement: HTMLVideoElement, 22 playerElement: HTMLVideoElement,
22 videoViewUrl: string, 23 videoViewUrl: string,
23 videoEmbedUrl: string,
24 videoDuration: number, 24 videoDuration: number,
25 videoFiles: VideoFile[], 25 videoFiles: VideoFile[],
26 enableHotkeys: boolean, 26 enableHotkeys: boolean,
@@ -43,29 +43,6 @@ function getVideojsOptions (options: {
43 videoViewUrl: options.videoViewUrl, 43 videoViewUrl: options.videoViewUrl,
44 videoDuration: options.videoDuration, 44 videoDuration: options.videoDuration,
45 startTime: options.startTime 45 startTime: options.startTime
46 },
47 contextmenuUI: {
48 content: [
49 {
50 label: 'Copy the video URL',
51 listener: function () {
52 copyToClipboard(buildVideoLink())
53 }
54 },
55 {
56 label: 'Copy the video URL at the current time',
57 listener: function () {
58 const player = this
59 copyToClipboard(buildVideoLink(player.currentTime()))
60 }
61 },
62 {
63 label: 'Copy embed code',
64 listener: () => {
65 copyToClipboard(buildVideoEmbed(options.videoEmbedUrl))
66 }
67 }
68 ]
69 } 46 }
70 }, 47 },
71 controlBar: { 48 controlBar: {
@@ -135,4 +112,44 @@ function getControlBarChildren (options: {
135 return children 112 return children
136} 113}
137 114
138export { getVideojsOptions } 115function addContextMenu (player: any, videoEmbedUrl: string) {
116 console.log(videoEmbedUrl)
117
118 player.contextmenuUI({
119 content: [
120 {
121 label: player.localize('Copy the video URL'),
122 listener: function () {
123 copyToClipboard(buildVideoLink())
124 }
125 },
126 {
127 label: player.localize('Copy the video URL at the current time'),
128 listener: function () {
129 const player = this
130 copyToClipboard(buildVideoLink(player.currentTime()))
131 }
132 },
133 {
134 label: player.localize('Copy embed code'),
135 listener: () => {
136 copyToClipboard(buildVideoEmbed(videoEmbedUrl))
137 }
138 }
139 ]
140 })
141}
142
143function loadLocale (serverUrl: string, videojs: any, locale: string) {
144 if (!is18nLocale(locale) || isDefaultLocale(locale)) return undefined
145
146 return fetch(serverUrl + '/client/locales/' + locale + '/player.json')
147 .then(res => res.json())
148 .then(json => videojs.addLanguage(locale, json))
149}
150
151export {
152 loadLocale,
153 getVideojsOptions,
154 addContextMenu
155}
diff --git a/client/src/assets/player/peertube-videojs-plugin.ts b/client/src/assets/player/peertube-videojs-plugin.ts
index 79df42a53..68e98f170 100644
--- a/client/src/assets/player/peertube-videojs-plugin.ts
+++ b/client/src/assets/player/peertube-videojs-plugin.ts
@@ -4,15 +4,7 @@ import { VideoFile } from '../../../../shared/models/videos/video.model'
4import { renderVideo } from './video-renderer' 4import { renderVideo } from './video-renderer'
5import './settings-menu-button' 5import './settings-menu-button'
6import { PeertubePluginOptions, VideoJSComponentInterface, videojsUntyped } from './peertube-videojs-typings' 6import { PeertubePluginOptions, VideoJSComponentInterface, videojsUntyped } from './peertube-videojs-typings'
7import { 7import { getAverageBandwidth, getStoredMute, getStoredVolume, saveAverageBandwidth, saveMuteInStore, saveVolumeInStore } from './utils'
8 getAverageBandwidth,
9 getStoredMute,
10 getStoredVolume,
11 isMobile,
12 saveAverageBandwidth,
13 saveMuteInStore,
14 saveVolumeInStore
15} from './utils'
16import minBy from 'lodash-es/minBy' 8import minBy from 'lodash-es/minBy'
17import maxBy from 'lodash-es/maxBy' 9import maxBy from 'lodash-es/maxBy'
18import * as CacheChunkStore from 'cache-chunk-store' 10import * as CacheChunkStore from 'cache-chunk-store'
diff --git a/client/src/assets/player/resolution-menu-button.ts b/client/src/assets/player/resolution-menu-button.ts
index 2efc8de69..d317a5efc 100644
--- a/client/src/assets/player/resolution-menu-button.ts
+++ b/client/src/assets/player/resolution-menu-button.ts
@@ -8,10 +8,7 @@ class ResolutionMenuButton extends MenuButton {
8 label: HTMLElement 8 label: HTMLElement
9 9
10 constructor (player: videojs.Player, options) { 10 constructor (player: videojs.Player, options) {
11 options.label = 'Quality'
12 super(player, options) 11 super(player, options)
13
14 this.controlText_ = 'Quality'
15 this.player = player 12 this.player = player
16 13
17 player.peertube().on('videoFileUpdate', () => this.updateLabel()) 14 player.peertube().on('videoFileUpdate', () => this.updateLabel())
@@ -51,7 +48,7 @@ class ResolutionMenuButton extends MenuButton {
51 this.player_, 48 this.player_,
52 { 49 {
53 id: -1, 50 id: -1,
54 label: 'Auto', 51 label: this.player_.localize('Auto'),
55 src: null 52 src: null
56 } 53 }
57 )) 54 ))
@@ -77,4 +74,6 @@ class ResolutionMenuButton extends MenuButton {
77 return this.player_.peertube().getCurrentResolutionLabel() 74 return this.player_.peertube().getCurrentResolutionLabel()
78 } 75 }
79} 76}
77ResolutionMenuButton.prototype.controlText_ = 'Quality'
78
80MenuButton.registerComponent('ResolutionMenuButton', ResolutionMenuButton) 79MenuButton.registerComponent('ResolutionMenuButton', ResolutionMenuButton)
diff --git a/client/src/assets/player/settings-menu-button.ts b/client/src/assets/player/settings-menu-button.ts
index bf6ac145a..b51c52506 100644
--- a/client/src/assets/player/settings-menu-button.ts
+++ b/client/src/assets/player/settings-menu-button.ts
@@ -275,7 +275,7 @@ class SettingsDialog extends Component {
275 275
276} 276}
277 277
278SettingsButton.prototype.controlText_ = 'Settings Button' 278SettingsButton.prototype.controlText_ = 'Settings'
279 279
280Component.registerComponent('SettingsButton', SettingsButton) 280Component.registerComponent('SettingsButton', SettingsButton)
281Component.registerComponent('SettingsDialog', SettingsDialog) 281Component.registerComponent('SettingsDialog', SettingsDialog)
diff --git a/client/src/assets/player/settings-menu-item.ts b/client/src/assets/player/settings-menu-item.ts
index 048c88533..f595fd459 100644
--- a/client/src/assets/player/settings-menu-item.ts
+++ b/client/src/assets/player/settings-menu-item.ts
@@ -132,7 +132,7 @@ class SettingsMenuItem extends MenuItem {
132 const button = this.subMenu.menu.addChild('MenuItem', {}, 0) 132 const button = this.subMenu.menu.addChild('MenuItem', {}, 0)
133 button.name_ = 'BackButton' 133 button.name_ = 'BackButton'
134 button.addClass('vjs-back-button') 134 button.addClass('vjs-back-button')
135 button.el_.innerHTML = this.subMenu.controlText_ 135 button.el_.innerHTML = this.player_.localize(this.subMenu.controlText_)
136 } 136 }
137 137
138 /** 138 /**
@@ -201,7 +201,7 @@ class SettingsMenuItem extends MenuItem {
201 saveUpdateLabel.call(this.subMenu) 201 saveUpdateLabel.call(this.subMenu)
202 } 202 }
203 203
204 this.settingsSubMenuTitleEl_.innerHTML = this.subMenu.controlText_ 204 this.settingsSubMenuTitleEl_.innerHTML = this.player_.localize(this.subMenu.controlText_)
205 this.settingsSubMenuEl_.appendChild(this.subMenu.menu.el_) 205 this.settingsSubMenuEl_.appendChild(this.subMenu.menu.el_)
206 this.panelChildEl.appendChild(this.settingsSubMenuEl_) 206 this.panelChildEl.appendChild(this.settingsSubMenuEl_)
207 this.update() 207 this.update()
diff --git a/client/src/assets/player/utils.ts b/client/src/assets/player/utils.ts
index 487b3a1be..ce7aaea2a 100644
--- a/client/src/assets/player/utils.ts
+++ b/client/src/assets/player/utils.ts
@@ -1,3 +1,5 @@
1import { is18nLocale, isDefaultLocale } from '../../../../shared/models/i18n/i18n'
2
1function toTitleCase (str: string) { 3function toTitleCase (str: string) {
2 return str.charAt(0).toUpperCase() + str.slice(1) 4 return str.charAt(0).toUpperCase() + str.slice(1)
3} 5}
diff --git a/client/src/assets/player/webtorrent-info-button.ts b/client/src/assets/player/webtorrent-info-button.ts
index baeb22b64..10945c665 100644
--- a/client/src/assets/player/webtorrent-info-button.ts
+++ b/client/src/assets/player/webtorrent-info-button.ts
@@ -60,13 +60,8 @@ class WebtorrentInfoButton extends Button {
60 className: 'peers-number', 60 className: 'peers-number',
61 textContent: 'HTTP' 61 textContent: 'HTTP'
62 }) 62 })
63 const subDivFallbackText = videojsUntyped.dom.createEl('span', {
64 className: 'peers-text',
65 textContent: ' fallback'
66 })
67 63
68 subDivHttp.appendChild(subDivHttpText) 64 subDivHttp.appendChild(subDivHttpText)
69 subDivHttp.appendChild(subDivFallbackText)
70 div.appendChild(subDivHttp) 65 div.appendChild(subDivHttp)
71 66
72 this.player_.peertube().on('torrentInfo', (event, data) => { 67 this.player_.peertube().on('torrentInfo', (event, data) => {
@@ -89,7 +84,7 @@ class WebtorrentInfoButton extends Button {
89 uploadSpeedUnit.textContent = ' ' + uploadSpeed[ 1 ] 84 uploadSpeedUnit.textContent = ' ' + uploadSpeed[ 1 ]
90 85
91 peersNumber.textContent = numPeers 86 peersNumber.textContent = numPeers
92 peersText.textContent = ' peers' 87 peersText.textContent = ' ' + this.player_.localize('peers')
93 88
94 subDivHttp.className = 'vjs-peertube-hidden' 89 subDivHttp.className = 'vjs-peertube-hidden'
95 subDivWebtorrent.className = 'vjs-peertube-displayed' 90 subDivWebtorrent.className = 'vjs-peertube-displayed'
diff --git a/client/src/locale/source/player_en_US.xml b/client/src/locale/source/player_en_US.xml
new file mode 100644
index 000000000..5bb6afdf7
--- /dev/null
+++ b/client/src/locale/source/player_en_US.xml
@@ -0,0 +1,378 @@
1<xliff xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="urn:oasis:names:tc:xliff:document:1.2 http://docs.oasis-open.org/xliff/v1.2/os/xliff-core-1.2-strict.xsd" xmlns="urn:oasis:names:tc:xliff:document:1.2" version="1.2">
2 <file original="namespace1" datatype="plaintext" source-language="undefined" target-language="undefined">
3 <body>
4 <trans-unit id="Audio Player">
5 <source>Audio Player</source>
6 <target>undefined</target>
7 </trans-unit>
8 <trans-unit id="Video Player">
9 <source>Video Player</source>
10 <target>undefined</target>
11 </trans-unit>
12 <trans-unit id="Play">
13 <source>Play</source>
14 <target>undefined</target>
15 </trans-unit>
16 <trans-unit id="Pause">
17 <source>Pause</source>
18 <target>undefined</target>
19 </trans-unit>
20 <trans-unit id="Replay">
21 <source>Replay</source>
22 <target>undefined</target>
23 </trans-unit>
24 <trans-unit id="Current Time">
25 <source>Current Time</source>
26 <target>undefined</target>
27 </trans-unit>
28 <trans-unit id="Duration">
29 <source>Duration</source>
30 <target>undefined</target>
31 </trans-unit>
32 <trans-unit id="Remaining Time">
33 <source>Remaining Time</source>
34 <target>undefined</target>
35 </trans-unit>
36 <trans-unit id="Stream Type">
37 <source>Stream Type</source>
38 <target>undefined</target>
39 </trans-unit>
40 <trans-unit id="LIVE">
41 <source>LIVE</source>
42 <target>undefined</target>
43 </trans-unit>
44 <trans-unit id="Loaded">
45 <source>Loaded</source>
46 <target>undefined</target>
47 </trans-unit>
48 <trans-unit id="Progress">
49 <source>Progress</source>
50 <target>undefined</target>
51 </trans-unit>
52 <trans-unit id="Progress Bar">
53 <source>Progress Bar</source>
54 <target>undefined</target>
55 </trans-unit>
56 <trans-unit id="progress bar timing: currentTime={1} duration={2}">
57 <source>{1} of {2}</source>
58 <target>undefined</target>
59 </trans-unit>
60 <trans-unit id="Fullscreen">
61 <source>Fullscreen</source>
62 <target>undefined</target>
63 </trans-unit>
64 <trans-unit id="Non-Fullscreen">
65 <source>Non-Fullscreen</source>
66 <target>undefined</target>
67 </trans-unit>
68 <trans-unit id="Mute">
69 <source>Mute</source>
70 <target>undefined</target>
71 </trans-unit>
72 <trans-unit id="Unmute">
73 <source>Unmute</source>
74 <target>undefined</target>
75 </trans-unit>
76 <trans-unit id="Playback Rate">
77 <source>Playback Rate</source>
78 <target>undefined</target>
79 </trans-unit>
80 <trans-unit id="Subtitles">
81 <source>Subtitles</source>
82 <target>undefined</target>
83 </trans-unit>
84 <trans-unit id="subtitles off">
85 <source>subtitles off</source>
86 <target>undefined</target>
87 </trans-unit>
88 <trans-unit id="Captions">
89 <source>Captions</source>
90 <target>undefined</target>
91 </trans-unit>
92 <trans-unit id="captions off">
93 <source>captions off</source>
94 <target>undefined</target>
95 </trans-unit>
96 <trans-unit id="Chapters">
97 <source>Chapters</source>
98 <target>undefined</target>
99 </trans-unit>
100 <trans-unit id="Descriptions">
101 <source>Descriptions</source>
102 <target>undefined</target>
103 </trans-unit>
104 <trans-unit id="descriptions off">
105 <source>descriptions off</source>
106 <target>undefined</target>
107 </trans-unit>
108 <trans-unit id="Audio Track">
109 <source>Audio Track</source>
110 <target>undefined</target>
111 </trans-unit>
112 <trans-unit id="Volume Level">
113 <source>Volume Level</source>
114 <target>undefined</target>
115 </trans-unit>
116 <trans-unit id="You aborted the media playback">
117 <source>You aborted the media playback</source>
118 <target>undefined</target>
119 </trans-unit>
120 <trans-unit id="A network error caused the media download to fail part-way.">
121 <source>A network error caused the media download to fail part-way.</source>
122 <target>undefined</target>
123 </trans-unit>
124 <trans-unit id="The media could not be loaded, either because the server or network failed or because the format is not supported.">
125 <source>The media could not be loaded, either because the server or network failed or because the format is not supported.</source>
126 <target>undefined</target>
127 </trans-unit>
128 <trans-unit id="The media playback was aborted due to a corruption problem or because the media used features your browser did not support.">
129 <source>The media playback was aborted due to a corruption problem or because the media used features your browser did not support.</source>
130 <target>undefined</target>
131 </trans-unit>
132 <trans-unit id="No compatible source was found for this media.">
133 <source>No compatible source was found for this media.</source>
134 <target>undefined</target>
135 </trans-unit>
136 <trans-unit id="The media is encrypted and we do not have the keys to decrypt it.">
137 <source>The media is encrypted and we do not have the keys to decrypt it.</source>
138 <target>undefined</target>
139 </trans-unit>
140 <trans-unit id="Play Video">
141 <source>Play Video</source>
142 <target>undefined</target>
143 </trans-unit>
144 <trans-unit id="Close">
145 <source>Close</source>
146 <target>undefined</target>
147 </trans-unit>
148 <trans-unit id="Close Modal Dialog">
149 <source>Close Modal Dialog</source>
150 <target>undefined</target>
151 </trans-unit>
152 <trans-unit id="Modal Window">
153 <source>Modal Window</source>
154 <target>undefined</target>
155 </trans-unit>
156 <trans-unit id="This is a modal window">
157 <source>This is a modal window</source>
158 <target>undefined</target>
159 </trans-unit>
160 <trans-unit id="This modal can be closed by pressing the Escape key or activating the close button.">
161 <source>This modal can be closed by pressing the Escape key or activating the close button.</source>
162 <target>undefined</target>
163 </trans-unit>
164 <trans-unit id=", opens captions settings dialog">
165 <source>, opens captions settings dialog</source>
166 <target>undefined</target>
167 </trans-unit>
168 <trans-unit id=", opens subtitles settings dialog">
169 <source>, opens subtitles settings dialog</source>
170 <target>undefined</target>
171 </trans-unit>
172 <trans-unit id=", opens descriptions settings dialog">
173 <source>, opens descriptions settings dialog</source>
174 <target>undefined</target>
175 </trans-unit>
176 <trans-unit id=", selected">
177 <source>, selected</source>
178 <target>undefined</target>
179 </trans-unit>
180 <trans-unit id="captions settings">
181 <source>captions settings</source>
182 <target>undefined</target>
183 </trans-unit>
184 <trans-unit id="subtitles settings">
185 <source>subititles settings</source>
186 <target>undefined</target>
187 </trans-unit>
188 <trans-unit id="descriptions settings">
189 <source>descriptions settings</source>
190 <target>undefined</target>
191 </trans-unit>
192 <trans-unit id="Text">
193 <source>Text</source>
194 <target>undefined</target>
195 </trans-unit>
196 <trans-unit id="White">
197 <source>White</source>
198 <target>undefined</target>
199 </trans-unit>
200 <trans-unit id="Black">
201 <source>Black</source>
202 <target>undefined</target>
203 </trans-unit>
204 <trans-unit id="Red">
205 <source>Red</source>
206 <target>undefined</target>
207 </trans-unit>
208 <trans-unit id="Green">
209 <source>Green</source>
210 <target>undefined</target>
211 </trans-unit>
212 <trans-unit id="Blue">
213 <source>Blue</source>
214 <target>undefined</target>
215 </trans-unit>
216 <trans-unit id="Yellow">
217 <source>Yellow</source>
218 <target>undefined</target>
219 </trans-unit>
220 <trans-unit id="Magenta">
221 <source>Magenta</source>
222 <target>undefined</target>
223 </trans-unit>
224 <trans-unit id="Cyan">
225 <source>Cyan</source>
226 <target>undefined</target>
227 </trans-unit>
228 <trans-unit id="Background">
229 <source>Background</source>
230 <target>undefined</target>
231 </trans-unit>
232 <trans-unit id="Window">
233 <source>Window</source>
234 <target>undefined</target>
235 </trans-unit>
236 <trans-unit id="Transparent">
237 <source>Transparent</source>
238 <target>undefined</target>
239 </trans-unit>
240 <trans-unit id="Semi-Transparent">
241 <source>Semi-Transparent</source>
242 <target>undefined</target>
243 </trans-unit>
244 <trans-unit id="Opaque">
245 <source>Opaque</source>
246 <target>undefined</target>
247 </trans-unit>
248 <trans-unit id="Font Size">
249 <source>Font Size</source>
250 <target>undefined</target>
251 </trans-unit>
252 <trans-unit id="Text Edge Style">
253 <source>Text Edge Style</source>
254 <target>undefined</target>
255 </trans-unit>
256 <trans-unit id="None">
257 <source>None</source>
258 <target>undefined</target>
259 </trans-unit>
260 <trans-unit id="Raised">
261 <source>Raised</source>
262 <target>undefined</target>
263 </trans-unit>
264 <trans-unit id="Depressed">
265 <source>Depressed</source>
266 <target>undefined</target>
267 </trans-unit>
268 <trans-unit id="Uniform">
269 <source>Uniform</source>
270 <target>undefined</target>
271 </trans-unit>
272 <trans-unit id="Dropshadow">
273 <source>Dropshadow</source>
274 <target>undefined</target>
275 </trans-unit>
276 <trans-unit id="Font Family">
277 <source>Font Family</source>
278 <target>undefined</target>
279 </trans-unit>
280 <trans-unit id="Proportional Sans-Serif">
281 <source>Proportional Sans-Serif</source>
282 <target>undefined</target>
283 </trans-unit>
284 <trans-unit id="Monospace Sans-Serif">
285 <source>Monospace Sans-Serif</source>
286 <target>undefined</target>
287 </trans-unit>
288 <trans-unit id="Proportional Serif">
289 <source>Proportional Serif</source>
290 <target>undefined</target>
291 </trans-unit>
292 <trans-unit id="Monospace Serif">
293 <source>Monospace Serif</source>
294 <target>undefined</target>
295 </trans-unit>
296 <trans-unit id="Casual">
297 <source>Casual</source>
298 <target>undefined</target>
299 </trans-unit>
300 <trans-unit id="Script">
301 <source>Script</source>
302 <target>undefined</target>
303 </trans-unit>
304 <trans-unit id="Small Caps">
305 <source>Small Caps</source>
306 <target>undefined</target>
307 </trans-unit>
308 <trans-unit id="Reset">
309 <source>Reset</source>
310 <target>undefined</target>
311 </trans-unit>
312 <trans-unit id="restore all settings to the default values">
313 <source>restore all settings to the default values</source>
314 <target>undefined</target>
315 </trans-unit>
316 <trans-unit id="Done">
317 <source>Done</source>
318 <target>undefined</target>
319 </trans-unit>
320 <trans-unit id="Caption Settings Dialog">
321 <source>Caption Settings Dialog</source>
322 <target>undefined</target>
323 </trans-unit>
324 <trans-unit id="Beginning of dialog window. Escape will cancel and close the window.">
325 <source>Beginning of dialog window. Escape will cancel and close the window.</source>
326 <target>undefined</target>
327 </trans-unit>
328 <trans-unit id="End of dialog window.">
329 <source>End of dialog window.</source>
330 <target>undefined</target>
331 </trans-unit>
332 <trans-unit id="{1} is loading.">
333 <source>{1} is loading.</source>
334 <target>undefined</target>
335 </trans-unit>
336 <trans-unit id="Quality">
337 <source>Quality</source>
338 <target>undefined</target>
339 </trans-unit>
340 <trans-unit id="Auto">
341 <source>Auto</source>
342 <target>undefined</target>
343 </trans-unit>
344 <trans-unit id="Speed">
345 <source>Speed</source>
346 <target>undefined</target>
347 </trans-unit>
348 <trans-unit id="peers">
349 <source>peers</source>
350 <target>undefined</target>
351 </trans-unit>
352 <trans-unit id="Go to the video page">
353 <source>Go to the video page</source>
354 <target>undefined</target>
355 </trans-unit>
356 <trans-unit id="Settings">
357 <source>Settings</source>
358 <target>undefined</target>
359 </trans-unit>
360 <trans-unit id="Uses P2P, others may know you are watching this video.">
361 <source>Uses P2P, others may know you are watching this video.</source>
362 <target>undefined</target>
363 </trans-unit>
364 <trans-unit id="Copy the video URL">
365 <source>Copy the video URL</source>
366 <target>undefined</target>
367 </trans-unit>
368 <trans-unit id="Copy the video URL at the current time">
369 <source>Copy the video URL at the current time</source>
370 <target>undefined</target>
371 </trans-unit>
372 <trans-unit id="Copy embed code">
373 <source>Copy embed code</source>
374 <target>undefined</target>
375 </trans-unit>
376 </body>
377 </file>
378</xliff> \ No newline at end of file
diff --git a/client/src/locale/source/videojs_en_US.json b/client/src/locale/source/videojs_en_US.json
new file mode 100644
index 000000000..92caaa683
--- /dev/null
+++ b/client/src/locale/source/videojs_en_US.json
@@ -0,0 +1,85 @@
1{
2 "Audio Player": "Audio Player",
3 "Video Player": "Video Player",
4 "Play": "Play",
5 "Pause": "Pause",
6 "Replay": "Replay",
7 "Current Time": "Current Time",
8 "Duration": "Duration",
9 "Remaining Time": "Remaining Time",
10 "Stream Type": "Stream Type",
11 "LIVE": "LIVE",
12 "Loaded": "Loaded",
13 "Progress": "Progress",
14 "Progress Bar": "Progress Bar",
15 "progress bar timing: currentTime={1} duration={2}": "{1} of {2}",
16 "Fullscreen": "Fullscreen",
17 "Non-Fullscreen": "Non-Fullscreen",
18 "Mute": "Mute",
19 "Unmute": "Unmute",
20 "Playback Rate": "Playback Rate",
21 "Subtitles": "Subtitles",
22 "subtitles off": "subtitles off",
23 "Captions": "Captions",
24 "captions off": "captions off",
25 "Chapters": "Chapters",
26 "Descriptions": "Descriptions",
27 "descriptions off": "descriptions off",
28 "Audio Track": "Audio Track",
29 "Volume Level": "Volume Level",
30 "You aborted the media playback": "You aborted the media playback",
31 "A network error caused the media download to fail part-way.": "A network error caused the media download to fail part-way.",
32 "The media could not be loaded, either because the server or network failed or because the format is not supported.": "The media could not be loaded, either because the server or network failed or because the format is not supported.",
33 "The media playback was aborted due to a corruption problem or because the media used features your browser did not support.": "The media playback was aborted due to a corruption problem or because the media used features your browser did not support.",
34 "No compatible source was found for this media.": "No compatible source was found for this media.",
35 "The media is encrypted and we do not have the keys to decrypt it.": "The media is encrypted and we do not have the keys to decrypt it.",
36 "Play Video": "Play Video",
37 "Close": "Close",
38 "Close Modal Dialog": "Close Modal Dialog",
39 "Modal Window": "Modal Window",
40 "This is a modal window": "This is a modal window",
41 "This modal can be closed by pressing the Escape key or activating the close button.": "This modal can be closed by pressing the Escape key or activating the close button.",
42 ", opens captions settings dialog": ", opens captions settings dialog",
43 ", opens subtitles settings dialog": ", opens subtitles settings dialog",
44 ", opens descriptions settings dialog": ", opens descriptions settings dialog",
45 ", selected": ", selected",
46 "captions settings": "captions settings",
47 "subtitles settings": "subititles settings",
48 "descriptions settings": "descriptions settings",
49 "Text": "Text",
50 "White": "White",
51 "Black": "Black",
52 "Red": "Red",
53 "Green": "Green",
54 "Blue": "Blue",
55 "Yellow": "Yellow",
56 "Magenta": "Magenta",
57 "Cyan": "Cyan",
58 "Background": "Background",
59 "Window": "Window",
60 "Transparent": "Transparent",
61 "Semi-Transparent": "Semi-Transparent",
62 "Opaque": "Opaque",
63 "Font Size": "Font Size",
64 "Text Edge Style": "Text Edge Style",
65 "None": "None",
66 "Raised": "Raised",
67 "Depressed": "Depressed",
68 "Uniform": "Uniform",
69 "Dropshadow": "Dropshadow",
70 "Font Family": "Font Family",
71 "Proportional Sans-Serif": "Proportional Sans-Serif",
72 "Monospace Sans-Serif": "Monospace Sans-Serif",
73 "Proportional Serif": "Proportional Serif",
74 "Monospace Serif": "Monospace Serif",
75 "Casual": "Casual",
76 "Script": "Script",
77 "Small Caps": "Small Caps",
78 "Reset": "Reset",
79 "restore all settings to the default values": "restore all settings to the default values",
80 "Done": "Done",
81 "Caption Settings Dialog": "Caption Settings Dialog",
82 "Beginning of dialog window. Escape will cancel and close the window.": "Beginning of dialog window. Escape will cancel and close the window.",
83 "End of dialog window.": "End of dialog window.",
84 "{1} is loading.": "{1} is loading."
85}
diff --git a/client/src/locale/target/player_fr.json b/client/src/locale/target/player_fr.json
new file mode 100644
index 000000000..6c399fc3f
--- /dev/null
+++ b/client/src/locale/target/player_fr.json
@@ -0,0 +1 @@
{"Audio Player":"Lecteur audio","Video Player":"Lecteur vidéo","Play":"Lecture","Pause":"Pause","Replay":"Revoir","Current Time":"Temps actuel","Duration":"Durée","Remaining Time":"Temps restant","Stream Type":"Type de flux","LIVE":"EN DIRECT","Loaded":"Chargé","Progress":"Progression","Progress Bar":"Barre de progression","progress bar timing: currentTime={1} duration={2}":"{1} de {2}","Fullscreen":"Plein écran","Non-Fullscreen":"Fenêtré","Mute":"Sourdine","Unmute":"Son activé","Playback Rate":"Vitesse de lecture","Subtitles":"Sous-titres","subtitles off":"Sous-titres désactivés","Captions":"Sous-titres transcrits","captions off":"Sous-titres transcrits désactivés","Chapters":"Chapitres","Descriptions":"Descriptions","descriptions off":"descriptions désactivées","Audio Track":"Piste audio","Volume Level":"Niveau de volume","You aborted the media playback":"Vous avez interrompu la lecture de la vidéo.","A network error caused the media download to fail part-way.":"Une erreur de réseau a interrompu le téléchargement de la vidéo.","The media could not be loaded, either because the server or network failed or because the format is not supported.":"Cette vidéo n'a pas pu être chargée, soit parce que le serveur ou le réseau a échoué ou parce que le format n'est pas reconnu.","The media playback was aborted due to a corruption problem or because the media used features your browser did not support.":"La lecture de la vidéo a été interrompue à cause d'un problème de corruption ou parce que la vidéo utilise des fonctionnalités non prises en charge par votre navigateur.","No compatible source was found for this media.":"Aucune source compatible n'a été trouvée pour cette vidéo.","The media is encrypted and we do not have the keys to decrypt it.":"Le média est chiffré et nous n'avons pas les clés pour le déchiffrer.","Play Video":"Lire la vidéo","Close":"Fermer","Close Modal Dialog":"Fermer la boîte de dialogue modale","Modal Window":"Fenêtre modale","This is a modal window":"Ceci est une fenêtre modale","This modal can be closed by pressing the Escape key or activating the close button.":"Ce modal peut être fermé en appuyant sur la touche Échap ou activer le bouton de fermeture.",", opens captions settings dialog":", ouvrir les paramètres des sous-titres transcrits",", opens subtitles settings dialog":", ouvrir les paramètres des sous-titres",", opens descriptions settings dialog":", ouvrir les paramètres des descriptions",", selected":", sélectionné","captions settings":"Paramètres des sous-titres transcrits","subtitles settings":"Paramètres des sous-titres","descriptions settings":"Paramètres des descriptions","Text":"Texte","White":"Blanc","Black":"Noir","Red":"Rouge","Green":"Vert","Blue":"Bleu","Yellow":"Jaune","Magenta":"Magenta","Cyan":"Cyan","Background":"Arrière-plan","Window":"Fenêtre","Transparent":"Transparent","Semi-Transparent":"Semi-transparent","Opaque":"Opaque","Font Size":"Taille des caractères","Text Edge Style":"Style des contours du texte","None":"Aucun","Raised":"Élevé","Depressed":"Enfoncé","Uniform":"Uniforme","Dropshadow":"Ombre portée","Font Family":"Familles de polices","Proportional Sans-Serif":"Polices à chasse variable sans empattement (Proportional Sans-Serif)","Monospace Sans-Serif":"Polices à chasse fixe sans empattement (Monospace Sans-Serif)","Proportional Serif":"Polices à chasse variable avec empattement (Proportional Serif)","Monospace Serif":"Polices à chasse fixe avec empattement (Monospace Serif)","Casual":"Manuscrite","Script":"Scripte","Small Caps":"Petites capitales","Reset":"Réinitialiser","restore all settings to the default values":"Restaurer tous les paramètres aux valeurs par défaut","Done":"Terminé","Caption Settings Dialog":"Boîte de dialogue des paramètres des sous-titres transcrits","Beginning of dialog window. Escape will cancel and close the window.":"Début de la fenêtre de dialogue. La touche d'échappement annulera et fermera la fenêtre.","End of dialog window.":"Fin de la fenêtre de dialogue.","{1} is loading.":"{1} est en train de charger","Quality":"Qualité","Auto":"Auto","Speed":"Vitesse","peers":"pairs","Go to the video page":"Aller sur la page de la vidéo","Settings":"Paramètres","Uses P2P, others may know you are watching this video.":"Utilise le P2P, d'autres personnes pourraient savoir que vous regardez cette vidéo.","Copy the video URL":"Copier le lien de la vidéo","Copy the video URL at the current time":"Copier le lien de la vidéo à partir de cette séquence","Copy embed code":"Copier le code d'intégration"} \ No newline at end of file
diff --git a/client/src/locale/target/player_fr.xml b/client/src/locale/target/player_fr.xml
new file mode 100644
index 000000000..eafa4baff
--- /dev/null
+++ b/client/src/locale/target/player_fr.xml
@@ -0,0 +1,379 @@
1<?xml version="1.0" encoding="utf-8"?>
2<!--XLIFF document generated by Zanata. Visit http://zanata.org for more infomation.-->
3<xliff xmlns="urn:oasis:names:tc:xliff:document:1.1" xmlns:xyz="urn:appInfo:Items" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="urn:oasis:names:tc:xliff:document:1.1 http://www.oasis-open.org/committees/xliff/documents/xliff-core-1.1.xsd" version="1.1">
4 <file source-language="en-US" datatype="plaintext" original="" target-language="fr">
5 <body>
6 <trans-unit id="Audio Player">
7 <source>Audio Player</source>
8 <target>Lecteur audio</target>
9 </trans-unit>
10 <trans-unit id="Video Player">
11 <source>Video Player</source>
12 <target>Lecteur vidéo</target>
13 </trans-unit>
14 <trans-unit id="Play">
15 <source>Play</source>
16 <target>Lecture</target>
17 </trans-unit>
18 <trans-unit id="Pause">
19 <source>Pause</source>
20 <target>Pause</target>
21 </trans-unit>
22 <trans-unit id="Replay">
23 <source>Replay</source>
24 <target>Revoir</target>
25 </trans-unit>
26 <trans-unit id="Current Time">
27 <source>Current Time</source>
28 <target>Temps actuel</target>
29 </trans-unit>
30 <trans-unit id="Duration">
31 <source>Duration</source>
32 <target>Durée</target>
33 </trans-unit>
34 <trans-unit id="Remaining Time">
35 <source>Remaining Time</source>
36 <target>Temps restant</target>
37 </trans-unit>
38 <trans-unit id="Stream Type">
39 <source>Stream Type</source>
40 <target>Type de flux</target>
41 </trans-unit>
42 <trans-unit id="LIVE">
43 <source>LIVE</source>
44 <target>EN DIRECT</target>
45 </trans-unit>
46 <trans-unit id="Loaded">
47 <source>Loaded</source>
48 <target>Chargé</target>
49 </trans-unit>
50 <trans-unit id="Progress">
51 <source>Progress</source>
52 <target>Progression</target>
53 </trans-unit>
54 <trans-unit id="Progress Bar">
55 <source>Progress Bar</source>
56 <target>Barre de progression</target>
57 </trans-unit>
58 <trans-unit id="progress bar timing: currentTime={1} duration={2}">
59 <source>{1} of {2}</source>
60 <target>{1} de {2}</target>
61 </trans-unit>
62 <trans-unit id="Fullscreen">
63 <source>Fullscreen</source>
64 <target>Plein écran</target>
65 </trans-unit>
66 <trans-unit id="Non-Fullscreen">
67 <source>Non-Fullscreen</source>
68 <target>Fenêtré</target>
69 </trans-unit>
70 <trans-unit id="Mute">
71 <source>Mute</source>
72 <target>Sourdine</target>
73 </trans-unit>
74 <trans-unit id="Unmute">
75 <source>Unmute</source>
76 <target>Son activé</target>
77 </trans-unit>
78 <trans-unit id="Playback Rate">
79 <source>Playback Rate</source>
80 <target>Vitesse de lecture</target>
81 </trans-unit>
82 <trans-unit id="Subtitles">
83 <source>Subtitles</source>
84 <target>Sous-titres</target>
85 </trans-unit>
86 <trans-unit id="subtitles off">
87 <source>subtitles off</source>
88 <target>Sous-titres désactivés</target>
89 </trans-unit>
90 <trans-unit id="Captions">
91 <source>Captions</source>
92 <target>Sous-titres transcrits</target>
93 </trans-unit>
94 <trans-unit id="captions off">
95 <source>captions off</source>
96 <target>Sous-titres transcrits désactivés</target>
97 </trans-unit>
98 <trans-unit id="Chapters">
99 <source>Chapters</source>
100 <target>Chapitres</target>
101 </trans-unit>
102 <trans-unit id="Descriptions">
103 <source>Descriptions</source>
104 <target>Descriptions</target>
105 </trans-unit>
106 <trans-unit id="descriptions off">
107 <source>descriptions off</source>
108 <target>descriptions désactivées</target>
109 </trans-unit>
110 <trans-unit id="Audio Track">
111 <source>Audio Track</source>
112 <target>Piste audio</target>
113 </trans-unit>
114 <trans-unit id="Volume Level">
115 <source>Volume Level</source>
116 <target>Niveau de volume</target>
117 </trans-unit>
118 <trans-unit id="You aborted the media playback">
119 <source>You aborted the media playback</source>
120 <target>Vous avez interrompu la lecture de la vidéo.</target>
121 </trans-unit>
122 <trans-unit id="A network error caused the media download to fail part-way.">
123 <source>A network error caused the media download to fail part-way.</source>
124 <target>Une erreur de réseau a interrompu le téléchargement de la vidéo.</target>
125 </trans-unit>
126 <trans-unit id="The media could not be loaded, either because the server or network failed or because the format is not supported.">
127 <source>The media could not be loaded, either because the server or network failed or because the format is not supported.</source>
128 <target>Cette vidéo n'a pas pu être chargée, soit parce que le serveur ou le réseau a échoué ou parce que le format n'est pas reconnu.</target>
129 </trans-unit>
130 <trans-unit id="The media playback was aborted due to a corruption problem or because the media used features your browser did not support.">
131 <source>The media playback was aborted due to a corruption problem or because the media used features your browser did not support.</source>
132 <target>La lecture de la vidéo a été interrompue à cause d'un problème de corruption ou parce que la vidéo utilise des fonctionnalités non prises en charge par votre navigateur.</target>
133 </trans-unit>
134 <trans-unit id="No compatible source was found for this media.">
135 <source>No compatible source was found for this media.</source>
136 <target>Aucune source compatible n'a été trouvée pour cette vidéo.</target>
137 </trans-unit>
138 <trans-unit id="The media is encrypted and we do not have the keys to decrypt it.">
139 <source>The media is encrypted and we do not have the keys to decrypt it.</source>
140 <target>Le média est chiffré et nous n'avons pas les clés pour le déchiffrer.</target>
141 </trans-unit>
142 <trans-unit id="Play Video">
143 <source>Play Video</source>
144 <target>Lire la vidéo</target>
145 </trans-unit>
146 <trans-unit id="Close">
147 <source>Close</source>
148 <target>Fermer</target>
149 </trans-unit>
150 <trans-unit id="Close Modal Dialog">
151 <source>Close Modal Dialog</source>
152 <target>Fermer la boîte de dialogue modale</target>
153 </trans-unit>
154 <trans-unit id="Modal Window">
155 <source>Modal Window</source>
156 <target>Fenêtre modale</target>
157 </trans-unit>
158 <trans-unit id="This is a modal window">
159 <source>This is a modal window</source>
160 <target>Ceci est une fenêtre modale</target>
161 </trans-unit>
162 <trans-unit id="This modal can be closed by pressing the Escape key or activating the close button.">
163 <source>This modal can be closed by pressing the Escape key or activating the close button.</source>
164 <target>Ce modal peut être fermé en appuyant sur la touche Échap ou activer le bouton de fermeture.</target>
165 </trans-unit>
166 <trans-unit id=", opens captions settings dialog">
167 <source>, opens captions settings dialog</source>
168 <target>, ouvrir les paramètres des sous-titres transcrits</target>
169 </trans-unit>
170 <trans-unit id=", opens subtitles settings dialog">
171 <source>, opens subtitles settings dialog</source>
172 <target>, ouvrir les paramètres des sous-titres</target>
173 </trans-unit>
174 <trans-unit id=", opens descriptions settings dialog">
175 <source>, opens descriptions settings dialog</source>
176 <target>, ouvrir les paramètres des descriptions</target>
177 </trans-unit>
178 <trans-unit id=", selected">
179 <source>, selected</source>
180 <target>, sélectionné</target>
181 </trans-unit>
182 <trans-unit id="captions settings">
183 <source>captions settings</source>
184 <target>Paramètres des sous-titres transcrits</target>
185 </trans-unit>
186 <trans-unit id="subtitles settings">
187 <source>subititles settings</source>
188 <target>Paramètres des sous-titres</target>
189 </trans-unit>
190 <trans-unit id="descriptions settings">
191 <source>descriptions settings</source>
192 <target>Paramètres des descriptions</target>
193 </trans-unit>
194 <trans-unit id="Text">
195 <source>Text</source>
196 <target>Texte</target>
197 </trans-unit>
198 <trans-unit id="White">
199 <source>White</source>
200 <target>Blanc</target>
201 </trans-unit>
202 <trans-unit id="Black">
203 <source>Black</source>
204 <target>Noir</target>
205 </trans-unit>
206 <trans-unit id="Red">
207 <source>Red</source>
208 <target>Rouge</target>
209 </trans-unit>
210 <trans-unit id="Green">
211 <source>Green</source>
212 <target>Vert</target>
213 </trans-unit>
214 <trans-unit id="Blue">
215 <source>Blue</source>
216 <target>Bleu</target>
217 </trans-unit>
218 <trans-unit id="Yellow">
219 <source>Yellow</source>
220 <target>Jaune</target>
221 </trans-unit>
222 <trans-unit id="Magenta">
223 <source>Magenta</source>
224 <target>Magenta</target>
225 </trans-unit>
226 <trans-unit id="Cyan">
227 <source>Cyan</source>
228 <target>Cyan</target>
229 </trans-unit>
230 <trans-unit id="Background">
231 <source>Background</source>
232 <target>Arrière-plan</target>
233 </trans-unit>
234 <trans-unit id="Window">
235 <source>Window</source>
236 <target>Fenêtre</target>
237 </trans-unit>
238 <trans-unit id="Transparent">
239 <source>Transparent</source>
240 <target>Transparent</target>
241 </trans-unit>
242 <trans-unit id="Semi-Transparent">
243 <source>Semi-Transparent</source>
244 <target>Semi-transparent</target>
245 </trans-unit>
246 <trans-unit id="Opaque">
247 <source>Opaque</source>
248 <target>Opaque</target>
249 </trans-unit>
250 <trans-unit id="Font Size">
251 <source>Font Size</source>
252 <target>Taille des caractères</target>
253 </trans-unit>
254 <trans-unit id="Text Edge Style">
255 <source>Text Edge Style</source>
256 <target>Style des contours du texte</target>
257 </trans-unit>
258 <trans-unit id="None">
259 <source>None</source>
260 <target>Aucun</target>
261 </trans-unit>
262 <trans-unit id="Raised">
263 <source>Raised</source>
264 <target>Élevé</target>
265 </trans-unit>
266 <trans-unit id="Depressed">
267 <source>Depressed</source>
268 <target>Enfoncé</target>
269 </trans-unit>
270 <trans-unit id="Uniform">
271 <source>Uniform</source>
272 <target>Uniforme</target>
273 </trans-unit>
274 <trans-unit id="Dropshadow">
275 <source>Dropshadow</source>
276 <target>Ombre portée</target>
277 </trans-unit>
278 <trans-unit id="Font Family">
279 <source>Font Family</source>
280 <target>Familles de polices</target>
281 </trans-unit>
282 <trans-unit id="Proportional Sans-Serif">
283 <source>Proportional Sans-Serif</source>
284 <target>Polices à chasse variable sans empattement (Proportional Sans-Serif)</target>
285 </trans-unit>
286 <trans-unit id="Monospace Sans-Serif">
287 <source>Monospace Sans-Serif</source>
288 <target>Polices à chasse fixe sans empattement (Monospace Sans-Serif)</target>
289 </trans-unit>
290 <trans-unit id="Proportional Serif">
291 <source>Proportional Serif</source>
292 <target>Polices à chasse variable avec empattement (Proportional Serif)</target>
293 </trans-unit>
294 <trans-unit id="Monospace Serif">
295 <source>Monospace Serif</source>
296 <target>Polices à chasse fixe avec empattement (Monospace Serif)</target>
297 </trans-unit>
298 <trans-unit id="Casual">
299 <source>Casual</source>
300 <target>Manuscrite</target>
301 </trans-unit>
302 <trans-unit id="Script">
303 <source>Script</source>
304 <target>Scripte</target>
305 </trans-unit>
306 <trans-unit id="Small Caps">
307 <source>Small Caps</source>
308 <target>Petites capitales</target>
309 </trans-unit>
310 <trans-unit id="Reset">
311 <source>Reset</source>
312 <target>Réinitialiser</target>
313 </trans-unit>
314 <trans-unit id="restore all settings to the default values">
315 <source>restore all settings to the default values</source>
316 <target>Restaurer tous les paramètres aux valeurs par défaut</target>
317 </trans-unit>
318 <trans-unit id="Done">
319 <source>Done</source>
320 <target>Terminé</target>
321 </trans-unit>
322 <trans-unit id="Caption Settings Dialog">
323 <source>Caption Settings Dialog</source>
324 <target>Boîte de dialogue des paramètres des sous-titres transcrits</target>
325 </trans-unit>
326 <trans-unit id="Beginning of dialog window. Escape will cancel and close the window.">
327 <source>Beginning of dialog window. Escape will cancel and close the window.</source>
328 <target>Début de la fenêtre de dialogue. La touche d'échappement annulera et fermera la fenêtre.</target>
329 </trans-unit>
330 <trans-unit id="End of dialog window.">
331 <source>End of dialog window.</source>
332 <target>Fin de la fenêtre de dialogue.</target>
333 </trans-unit>
334 <trans-unit id="{1} is loading.">
335 <source>{1} is loading.</source>
336 <target>{1} est en train de charger</target>
337 </trans-unit>
338 <trans-unit id="Quality">
339 <source>Quality</source>
340 <target>Qualité</target>
341 </trans-unit>
342 <trans-unit id="Auto">
343 <source>Auto</source>
344 <target>Auto</target>
345 </trans-unit>
346 <trans-unit id="Speed">
347 <source>Speed</source>
348 <target>Vitesse</target>
349 </trans-unit>
350 <trans-unit id="peers">
351 <source>peers</source>
352 <target>pairs</target>
353 </trans-unit>
354 <trans-unit id="Go to the video page">
355 <source>Go to the video page</source>
356 <target>Aller sur la page de la vidéo</target>
357 </trans-unit>
358 <trans-unit id="Settings">
359 <source>Settings</source>
360 <target>Paramètres</target>
361 </trans-unit>
362 <trans-unit id="Uses P2P, others may know you are watching this video.">
363 <source>Uses P2P, others may know you are watching this video.</source>
364 <target>Utilise le P2P, d'autres personnes pourraient savoir que vous regardez cette vidéo.</target>
365 </trans-unit>
366 <trans-unit id="Copy the video URL">
367 <source>Copy the video URL</source>
368 <target>Copier le lien de la vidéo</target>
369 </trans-unit>
370 <trans-unit id="Copy the video URL at the current time">
371 <source>Copy the video URL at the current time</source>
372 <target>Copier le lien de la vidéo à partir de cette séquence</target>
373 </trans-unit>
374 <trans-unit id="Copy embed code">
375 <source>Copy embed code</source>
376 <target>Copier le code d'intégration</target>
377 </trans-unit>
378 </body>
379 </file></xliff> \ No newline at end of file
diff --git a/client/src/standalone/videos/embed.ts b/client/src/standalone/videos/embed.ts
index d603690ca..166013226 100644
--- a/client/src/standalone/videos/embed.ts
+++ b/client/src/standalone/videos/embed.ts
@@ -14,14 +14,14 @@ import 'core-js/es6/regexp'
14import 'core-js/es6/map' 14import 'core-js/es6/map'
15import 'core-js/es6/weak-map' 15import 'core-js/es6/weak-map'
16import 'core-js/es6/set' 16import 'core-js/es6/set'
17
18// For google bot that uses Chrome 41 and does not understand fetch 17// For google bot that uses Chrome 41 and does not understand fetch
19import 'whatwg-fetch' 18import 'whatwg-fetch'
20 19
21import * as videojs from 'video.js' 20import * as videojs from 'video.js'
22 21
23import { VideoDetails } from '../../../../shared' 22import { VideoDetails } from '../../../../shared'
24import { getVideojsOptions } from '../../assets/player/peertube-player' 23import { addContextMenu, getVideojsOptions, loadLocale } from '../../assets/player/peertube-player'
24import { environment } from '../../environments/environment'
25 25
26function getVideoUrl (id: string) { 26function getVideoUrl (id: string) {
27 return window.location.origin + '/api/v1/videos/' + id 27 return window.location.origin + '/api/v1/videos/' + id
@@ -61,7 +61,8 @@ function videoFetchError (videoElement: HTMLVideoElement) {
61const urlParts = window.location.href.split('/') 61const urlParts = window.location.href.split('/')
62const videoId = urlParts[urlParts.length - 1] 62const videoId = urlParts[urlParts.length - 1]
63 63
64loadVideoInfo(videoId) 64loadLocale(environment.apiUrl, videojs, navigator.language)
65 .then(() => loadVideoInfo(videoId))
65 .then(async response => { 66 .then(async response => {
66 const videoContainerId = 'video-container' 67 const videoContainerId = 'video-container'
67 const videoElement = document.getElementById(videoContainerId) as HTMLVideoElement 68 const videoElement = document.getElementById(videoContainerId) as HTMLVideoElement
@@ -91,7 +92,6 @@ loadVideoInfo(videoId)
91 const videojsOptions = getVideojsOptions({ 92 const videojsOptions = getVideojsOptions({
92 autoplay, 93 autoplay,
93 inactivityTimeout: 1500, 94 inactivityTimeout: 1500,
94 videoEmbedUrl: window.location.origin + videoInfo.embedPath,
95 videoViewUrl: getVideoUrl(videoId) + '/views', 95 videoViewUrl: getVideoUrl(videoId) + '/views',
96 playerElement: videoElement, 96 playerElement: videoElement,
97 videoFiles: videoInfo.files, 97 videoFiles: videoInfo.files,
@@ -106,8 +106,10 @@ loadVideoInfo(videoId)
106 106
107 player.dock({ 107 player.dock({
108 title: videoInfo.name, 108 title: videoInfo.name,
109 description: 'Uses P2P, others may know you are watching this video.' 109 description: player.localize('Uses P2P, others may know you are watching this video.')
110 }) 110 })
111
112 addContextMenu(player, window.location.origin + videoInfo.embedPath)
111 }) 113 })
112 }) 114 })
113 .catch(err => console.error(err)) 115 .catch(err => console.error(err))
diff --git a/package.json b/package.json
index 21701e664..707579af3 100644
--- a/package.json
+++ b/package.json
@@ -30,6 +30,8 @@
30 "danger:clean:prod": "scripty", 30 "danger:clean:prod": "scripty",
31 "danger:clean:modules": "scripty", 31 "danger:clean:modules": "scripty",
32 "i18n:generate": "scripty", 32 "i18n:generate": "scripty",
33 "i18n:xliff2json": "node ./dist/scripts/i18n/xliff2json.js",
34 "i18n:create-custom-files": "node ./dist/scripts/i18n/create-custom-files.js",
33 "reset-password": "node ./dist/scripts/reset-password.js", 35 "reset-password": "node ./dist/scripts/reset-password.js",
34 "play": "scripty", 36 "play": "scripty",
35 "dev": "scripty", 37 "dev": "scripty",
@@ -174,6 +176,7 @@
174 "tslint-config-standard": "^7.0.0", 176 "tslint-config-standard": "^7.0.0",
175 "typescript": "^2.5.2", 177 "typescript": "^2.5.2",
176 "webtorrent": "^0.100.0", 178 "webtorrent": "^0.100.0",
179 "xliff": "^3.0.1",
177 "youtube-dl": "^1.12.2" 180 "youtube-dl": "^1.12.2"
178 }, 181 },
179 "scripty": { 182 "scripty": {
diff --git a/scripts/i18n/create-custom-files.ts b/scripts/i18n/create-custom-files.ts
new file mode 100755
index 000000000..3895b3b9d
--- /dev/null
+++ b/scripts/i18n/create-custom-files.ts
@@ -0,0 +1,49 @@
1import * as jsToXliff12 from 'xliff/jsToXliff12'
2import { writeFile } from 'fs'
3import { join } from 'path'
4
5// First, the player
6const playerSource = join(__dirname, '../../../client/src/locale/source/videojs_en_US.json')
7const playerTarget = join(__dirname, '../../../client/src/locale/source/player_en_US.xml')
8
9const videojs = require(playerSource)
10const playerKeys = {
11 'Quality': 'Quality',
12 'Auto': 'Auto',
13 'Speed': 'Speed',
14 'peers': 'peers',
15 'Go to the video page': 'Go to the video page',
16 'Settings': 'Settings',
17 'Uses P2P, others may know you are watching this video.': 'Uses P2P, others may know you are watching this video.',
18 'Copy the video URL': 'Copy the video URL',
19 'Copy the video URL at the current time': 'Copy the video URL at the current time',
20 'Copy embed code': 'Copy embed code'
21}
22
23const obj = {
24 resources: {
25 namespace1: {}
26 }
27}
28
29for (const sourceObject of [ videojs, playerKeys ]) {
30 Object.keys(sourceObject).forEach(k => obj.resources.namespace1[ k ] = { source: sourceObject[ k ] })
31}
32
33jsToXliff12(obj, (err, res) => {
34 if (err) {
35 console.error(err)
36 process.exit(-1)
37 }
38
39 writeFile(playerTarget, res, err => {
40 if (err) {
41 console.error(err)
42 process.exit(-1)
43 }
44
45 process.exit(0)
46 })
47})
48
49// Then, the server strings
diff --git a/scripts/i18n/generate.sh b/scripts/i18n/generate.sh
index f8ad8a3c1..0a5b6dee1 100755
--- a/scripts/i18n/generate.sh
+++ b/scripts/i18n/generate.sh
@@ -9,4 +9,8 @@ npm run ngx-extractor -- --locale "en-US" -i 'src/**/*.ts' -f xlf -o src/locale/
9# Zanata does not support inner elements in <source>, so we hack these special elements 9# Zanata does not support inner elements in <source>, so we hack these special elements
10# This regex translate the Angular elements to special entities (that we will reconvert on pull) 10# This regex translate the Angular elements to special entities (that we will reconvert on pull)
11#sed -i 's/<x id=\(.\+\?\)\/>/\&lt;x id=\1\/\&gt;/g' src/locale/source/messages_en_US.xml 11#sed -i 's/<x id=\(.\+\?\)\/>/\&lt;x id=\1\/\&gt;/g' src/locale/source/messages_en_US.xml
12perl -pi -e 's|<x id=(.+?)/>|&lt;x id=\1/&gt;|g' src/locale/source/messages_en_US.xml \ No newline at end of file 12perl -pi -e 's|<x id=(.+?)/>|&lt;x id=\1/&gt;|g' src/locale/source/messages_en_US.xml
13
14# Add our strings too
15cd ../
16npm run i18n:create-custom-files \ No newline at end of file
diff --git a/scripts/i18n/pull-hook.sh b/scripts/i18n/pull-hook.sh
index bbe4a813e..dec426b88 100755
--- a/scripts/i18n/pull-hook.sh
+++ b/scripts/i18n/pull-hook.sh
@@ -7,5 +7,7 @@ set -eu
7#sed -i 's/\&lt;x id=\(.\+\?\)\/\&gt;/<x id=\1\/>/g' client/src/locale/target/* 7#sed -i 's/\&lt;x id=\(.\+\?\)\/\&gt;/<x id=\1\/>/g' client/src/locale/target/*
8 8
9for i in 1 2 3; do 9for i in 1 2 3; do
10 perl -pi -e 's|&lt;x id=(.+?)/&gt;([^"])|<x id=\1/>\2|g' client/src/locale/target/* 10 perl -pi -e 's|&lt;x id=(.+?)/&gt;([^"])|<x id=\1/>\2|g' client/src/locale/target/*.xml
11done \ No newline at end of file 11done
12
13npm run i18n:xliff2json \ No newline at end of file
diff --git a/scripts/i18n/xliff2json.ts b/scripts/i18n/xliff2json.ts
new file mode 100755
index 000000000..34784ac11
--- /dev/null
+++ b/scripts/i18n/xliff2json.ts
@@ -0,0 +1,42 @@
1import * as xliff12ToJs from 'xliff/xliff12ToJs'
2import { readFileSync, writeFile } from 'fs'
3import { join } from 'path'
4
5// First, the player
6const playerSource = join(__dirname, '../../../client/src/locale/target/player_fr.xml')
7const playerTarget = join(__dirname, '../../../client/src/locale/target/player_fr.json')
8
9// Remove the two first lines our xliff module does not like
10let playerFile = readFileSync(playerSource).toString()
11playerFile = removeFirstLine(playerFile)
12playerFile = removeFirstLine(playerFile)
13
14xliff12ToJs(playerFile, (err, res) => {
15 if (err) {
16 console.error(err)
17 process.exit(-1)
18 }
19
20 const json = createJSONString(res)
21 writeFile(playerTarget, json, err => {
22 if (err) {
23 console.error(err)
24 process.exit(-1)
25 }
26
27 process.exit(0)
28 })
29})
30
31function removeFirstLine (str: string) {
32 return str.substring(str.indexOf('\n') + 1)
33}
34
35function createJSONString (obj: any) {
36 const res: any = {}
37 const strings = obj.resources['']
38
39 Object.keys(strings).forEach(k => res[k] = strings[k].target)
40
41 return JSON.stringify(res)
42}
diff --git a/scripts/watch/server.sh b/scripts/watch/server.sh
index 6250fb9a4..badbf3da0 100755
--- a/scripts/watch/server.sh
+++ b/scripts/watch/server.sh
@@ -2,6 +2,11 @@
2 2
3set -eu 3set -eu
4 4
5# Copy locales
6mkdir -p "./client/dist"
7rm -r "./client/dist/locale"
8cp -r "./client/src/locale/target" "./client/dist/locale"
9
5NODE_ENV=test concurrently -k \ 10NODE_ENV=test concurrently -k \
6 "npm run tsc -- --sourceMap && npm run nodemon -- --delay 2 --watch ./dist dist/server" \ 11 "npm run tsc -- --sourceMap && npm run nodemon -- --delay 2 --watch ./dist dist/server" \
7 "npm run tsc -- --sourceMap --preserveWatchOutput -w" 12 "npm run tsc -- --sourceMap --preserveWatchOutput -w"
diff --git a/server/controllers/client.ts b/server/controllers/client.ts
index 4b37b5fa6..b153f6086 100644
--- a/server/controllers/client.ts
+++ b/server/controllers/client.ts
@@ -47,6 +47,14 @@ for (const staticClientFile of staticClientFiles) {
47clientsRouter.use('/client', express.static(distPath, { maxAge: STATIC_MAX_AGE })) 47clientsRouter.use('/client', express.static(distPath, { maxAge: STATIC_MAX_AGE }))
48clientsRouter.use('/client/assets/images', express.static(assetsImagesPath, { maxAge: STATIC_MAX_AGE })) 48clientsRouter.use('/client/assets/images', express.static(assetsImagesPath, { maxAge: STATIC_MAX_AGE }))
49 49
50clientsRouter.use('/client/locales/:locale/:file.json', function (req, res) {
51 if (req.params.locale === 'fr' && req.params.file === 'player') {
52 return res.sendFile(join(__dirname, '../../../client/dist/locale/player_fr.json'))
53 }
54
55 return res.sendStatus(404)
56})
57
50// 404 for static files not found 58// 404 for static files not found
51clientsRouter.use('/client/*', (req: express.Request, res: express.Response, next: express.NextFunction) => { 59clientsRouter.use('/client/*', (req: express.Request, res: express.Response, next: express.NextFunction) => {
52 res.sendStatus(404) 60 res.sendStatus(404)
diff --git a/shared/models/i18n/i18n.ts b/shared/models/i18n/i18n.ts
index 2d3a1d3e2..4d50bc36e 100644
--- a/shared/models/i18n/i18n.ts
+++ b/shared/models/i18n/i18n.ts
@@ -7,6 +7,10 @@ export function getDefaultLocale () {
7 return 'en-US' 7 return 'en-US'
8} 8}
9 9
10export function isDefaultLocale (locale: string) {
11 return locale === getDefaultLocale()
12}
13
10const possiblePaths = Object.keys(I18N_LOCALES).map(l => '/' + l) 14const possiblePaths = Object.keys(I18N_LOCALES).map(l => '/' + l)
11export function is18nPath (path: string) { 15export function is18nPath (path: string) {
12 return possiblePaths.indexOf(path) !== -1 16 return possiblePaths.indexOf(path) !== -1
diff --git a/yarn.lock b/yarn.lock
index eb06faac0..3b6c7574e 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -8402,6 +8402,18 @@ xhr2@^0.1.4:
8402 version "0.1.4" 8402 version "0.1.4"
8403 resolved "https://registry.yarnpkg.com/xhr2/-/xhr2-0.1.4.tgz#7f87658847716db5026323812f818cadab387a5f" 8403 resolved "https://registry.yarnpkg.com/xhr2/-/xhr2-0.1.4.tgz#7f87658847716db5026323812f818cadab387a5f"
8404 8404
8405xliff@^3.0.1:
8406 version "3.0.1"
8407 resolved "https://registry.yarnpkg.com/xliff/-/xliff-3.0.1.tgz#ea0f5840011727aecbddf111e5c26d8590dcca9b"
8408 dependencies:
8409 xml-js "1.6.2"
8410
8411xml-js@1.6.2:
8412 version "1.6.2"
8413 resolved "https://registry.yarnpkg.com/xml-js/-/xml-js-1.6.2.tgz#4c4cb8413998f73701a202a1b8b2f17c985a72c5"
8414 dependencies:
8415 sax "^1.2.4"
8416
8405xml-name-validator@^3.0.0: 8417xml-name-validator@^3.0.0:
8406 version "3.0.0" 8418 version "3.0.0"
8407 resolved "https://registry.yarnpkg.com/xml-name-validator/-/xml-name-validator-3.0.0.tgz#6ae73e06de4d8c6e47f9fb181f78d648ad457c6a" 8419 resolved "https://registry.yarnpkg.com/xml-name-validator/-/xml-name-validator-3.0.0.tgz#6ae73e06de4d8c6e47f9fb181f78d648ad457c6a"