Localize player
authorChocobozzz <me@florianbigard.com>
Wed, 6 Jun 2018 12:23:40 +0000 (14:23 +0200)
committerChocobozzz <me@florianbigard.com>
Wed, 6 Jun 2018 14:48:40 +0000 (16:48 +0200)
23 files changed:
client/src/app/videos/+video-watch/video-watch.component.ts
client/src/assets/player/peertube-link-button.ts
client/src/assets/player/peertube-player.ts
client/src/assets/player/peertube-videojs-plugin.ts
client/src/assets/player/resolution-menu-button.ts
client/src/assets/player/settings-menu-button.ts
client/src/assets/player/settings-menu-item.ts
client/src/assets/player/utils.ts
client/src/assets/player/webtorrent-info-button.ts
client/src/locale/source/player_en_US.xml [new file with mode: 0644]
client/src/locale/source/videojs_en_US.json [new file with mode: 0644]
client/src/locale/target/player_fr.json [new file with mode: 0644]
client/src/locale/target/player_fr.xml [new file with mode: 0644]
client/src/standalone/videos/embed.ts
package.json
scripts/i18n/create-custom-files.ts [new file with mode: 0755]
scripts/i18n/generate.sh
scripts/i18n/pull-hook.sh
scripts/i18n/xliff2json.ts [new file with mode: 0755]
scripts/watch/server.sh
server/controllers/client.ts
shared/models/i18n/i18n.ts
yarn.lock

index 23d74494c5c599ef5e6e90634e0dada78d937052..d3e16c4cfb92c4fa5cb20b0c2ad6974901330dab 100644 (file)
@@ -1,5 +1,5 @@
 import { catchError } from 'rxjs/operators'
-import { Component, ElementRef, NgZone, OnDestroy, OnInit, ViewChild } from '@angular/core'
+import { Component, ElementRef, LOCALE_ID, NgZone, OnDestroy, OnInit, ViewChild, Inject } from '@angular/core'
 import { ActivatedRoute, Router } from '@angular/router'
 import { RedirectService } from '@app/core/routing/redirect.service'
 import { peertubeLocalStorage } from '@app/shared/misc/peertube-local-storage'
@@ -21,9 +21,10 @@ import { MarkdownService } from '../shared'
 import { VideoDownloadComponent } from './modal/video-download.component'
 import { VideoReportComponent } from './modal/video-report.component'
 import { VideoShareComponent } from './modal/video-share.component'
-import { getVideojsOptions } from '../../../assets/player/peertube-player'
+import { getVideojsOptions, loadLocale, addContextMenu } from '../../../assets/player/peertube-player'
 import { ServerService } from '@app/core'
 import { I18n } from '@ngx-translate/i18n-polyfill'
+import { environment } from '../../../environments/environment'
 
 @Component({
   selector: 'my-video-watch',
@@ -54,6 +55,7 @@ export class VideoWatchComponent implements OnInit, OnDestroy {
   likesBarTooltipText = ''
   hasAlreadyAcceptedPrivacyConcern = false
 
+  private videojsLocaleLoaded = false
   private otherVideos: Video[] = []
   private paramsSub: Subscription
 
@@ -72,7 +74,8 @@ export class VideoWatchComponent implements OnInit, OnDestroy {
     private markdownService: MarkdownService,
     private zone: NgZone,
     private redirectService: RedirectService,
-    private i18n: I18n
+    private i18n: I18n,
+    @Inject(LOCALE_ID) private localeId: string
   ) {}
 
   get user () {
@@ -365,7 +368,6 @@ export class VideoWatchComponent implements OnInit, OnDestroy {
       inactivityTimeout: 2500,
       videoFiles: this.video.files,
       playerElement: this.playerElement,
-      videoEmbedUrl: this.video.embedUrl,
       videoViewUrl: this.videoService.getVideoViewUrl(this.video.uuid),
       videoDuration: this.video.duration,
       enableHotkeys: true,
@@ -374,11 +376,18 @@ export class VideoWatchComponent implements OnInit, OnDestroy {
       startTime
     })
 
+    if (this.videojsLocaleLoaded === false) {
+      await loadLocale(environment.apiUrl, videojs, environment.production === true ? this.localeId : 'fr')
+      this.videojsLocaleLoaded = true
+    }
+
     const self = this
-    this.zone.runOutsideAngular(() => {
+    this.zone.runOutsideAngular(async () => {
       videojs(this.playerElement, videojsOptions, function () {
         self.player = this
         this.on('customError', (event, data) => self.handleError(data.err))
+
+        addContextMenu(self.player, self.video.embedUrl)
       })
     })
 
index a13815d611206388395186193a32654bdd72c55b..26f8b9d732d7b9d8f0bb58a9eddf1d792a408c54 100644 (file)
@@ -24,7 +24,7 @@ class PeerTubeLinkButton extends Button {
     const el = videojsUntyped.dom.createEl('a', {
       href: buildVideoLink(),
       innerHTML: 'PeerTube',
-      title: 'Go to the video page',
+      title: this.player_.localize('Go to the video page'),
       className: 'vjs-peertube-link',
       target: '_blank'
     })
index d204b9703ea5094a89be8f1f36c1c5f5d7102b9b..b604097fa664a0746bd2433d8e2ae04d2b4b1d76 100644 (file)
@@ -12,6 +12,7 @@ import './peertube-videojs-plugin'
 import './peertube-load-progress-bar'
 import { videojsUntyped } from './peertube-videojs-typings'
 import { buildVideoEmbed, buildVideoLink, copyToClipboard } from './utils'
+import { is18nLocale, isDefaultLocale } from '../../../../shared/models/i18n/i18n'
 
 // Change 'Playback Rate' to 'Speed' (smaller for our settings menu)
 videojsUntyped.getComponent('PlaybackRateMenuButton').prototype.controlText_ = 'Speed'
@@ -20,7 +21,6 @@ function getVideojsOptions (options: {
   autoplay: boolean,
   playerElement: HTMLVideoElement,
   videoViewUrl: string,
-  videoEmbedUrl: string,
   videoDuration: number,
   videoFiles: VideoFile[],
   enableHotkeys: boolean,
@@ -43,29 +43,6 @@ function getVideojsOptions (options: {
         videoViewUrl: options.videoViewUrl,
         videoDuration: options.videoDuration,
         startTime: options.startTime
-      },
-      contextmenuUI: {
-        content: [
-          {
-            label: 'Copy the video URL',
-            listener: function () {
-              copyToClipboard(buildVideoLink())
-            }
-          },
-          {
-            label: 'Copy the video URL at the current time',
-            listener: function () {
-              const player = this
-              copyToClipboard(buildVideoLink(player.currentTime()))
-            }
-          },
-          {
-            label: 'Copy embed code',
-            listener: () => {
-              copyToClipboard(buildVideoEmbed(options.videoEmbedUrl))
-            }
-          }
-        ]
       }
     },
     controlBar: {
@@ -135,4 +112,44 @@ function getControlBarChildren (options: {
   return children
 }
 
-export { getVideojsOptions }
+function addContextMenu (player: any, videoEmbedUrl: string) {
+  console.log(videoEmbedUrl)
+
+  player.contextmenuUI({
+    content: [
+      {
+        label: player.localize('Copy the video URL'),
+        listener: function () {
+          copyToClipboard(buildVideoLink())
+        }
+      },
+      {
+        label: player.localize('Copy the video URL at the current time'),
+        listener: function () {
+          const player = this
+          copyToClipboard(buildVideoLink(player.currentTime()))
+        }
+      },
+      {
+        label: player.localize('Copy embed code'),
+        listener: () => {
+          copyToClipboard(buildVideoEmbed(videoEmbedUrl))
+        }
+      }
+    ]
+  })
+}
+
+function loadLocale (serverUrl: string, videojs: any, locale: string) {
+  if (!is18nLocale(locale) || isDefaultLocale(locale)) return undefined
+
+  return fetch(serverUrl + '/client/locales/' + locale + '/player.json')
+    .then(res => res.json())
+    .then(json => videojs.addLanguage(locale, json))
+}
+
+export {
+  loadLocale,
+  getVideojsOptions,
+  addContextMenu
+}
index 79df42a533783f81269f1746a49bb8c75b8f7139..68e98f17048bdea322966f4691f38bbf3190eda8 100644 (file)
@@ -4,15 +4,7 @@ import { VideoFile } from '../../../../shared/models/videos/video.model'
 import { renderVideo } from './video-renderer'
 import './settings-menu-button'
 import { PeertubePluginOptions, VideoJSComponentInterface, videojsUntyped } from './peertube-videojs-typings'
-import {
-  getAverageBandwidth,
-  getStoredMute,
-  getStoredVolume,
-  isMobile,
-  saveAverageBandwidth,
-  saveMuteInStore,
-  saveVolumeInStore
-} from './utils'
+import { getAverageBandwidth, getStoredMute, getStoredVolume, saveAverageBandwidth, saveMuteInStore, saveVolumeInStore } from './utils'
 import minBy from 'lodash-es/minBy'
 import maxBy from 'lodash-es/maxBy'
 import * as CacheChunkStore from 'cache-chunk-store'
index 2efc8de69603a9796e2dfd57cf983831bc91415e..d317a5efc971b6e0aa28bffbc32e0d17d249fea4 100644 (file)
@@ -8,10 +8,7 @@ class ResolutionMenuButton extends MenuButton {
   label: HTMLElement
 
   constructor (player: videojs.Player, options) {
-    options.label = 'Quality'
     super(player, options)
-
-    this.controlText_ = 'Quality'
     this.player = player
 
     player.peertube().on('videoFileUpdate', () => this.updateLabel())
@@ -51,7 +48,7 @@ class ResolutionMenuButton extends MenuButton {
       this.player_,
       {
         id: -1,
-        label: 'Auto',
+        label: this.player_.localize('Auto'),
         src: null
       }
     ))
@@ -77,4 +74,6 @@ class ResolutionMenuButton extends MenuButton {
     return this.player_.peertube().getCurrentResolutionLabel()
   }
 }
+ResolutionMenuButton.prototype.controlText_ = 'Quality'
+
 MenuButton.registerComponent('ResolutionMenuButton', ResolutionMenuButton)
index bf6ac145a9ca191c2a93bbdf80358416e04722d3..b51c52506b44ed131281049dc94caa1aaed917bb 100644 (file)
@@ -275,7 +275,7 @@ class SettingsDialog extends Component {
 
 }
 
-SettingsButton.prototype.controlText_ = 'Settings Button'
+SettingsButton.prototype.controlText_ = 'Settings'
 
 Component.registerComponent('SettingsButton', SettingsButton)
 Component.registerComponent('SettingsDialog', SettingsDialog)
index 048c8853309e6e1947922d293990d64bdb7f5182..f595fd459459ae2eb6c909c277754017fe45e9b4 100644 (file)
@@ -132,7 +132,7 @@ class SettingsMenuItem extends MenuItem {
     const button = this.subMenu.menu.addChild('MenuItem', {}, 0)
     button.name_ = 'BackButton'
     button.addClass('vjs-back-button')
-    button.el_.innerHTML = this.subMenu.controlText_
+    button.el_.innerHTML = this.player_.localize(this.subMenu.controlText_)
   }
 
   /**
@@ -201,7 +201,7 @@ class SettingsMenuItem extends MenuItem {
       saveUpdateLabel.call(this.subMenu)
     }
 
-    this.settingsSubMenuTitleEl_.innerHTML = this.subMenu.controlText_
+    this.settingsSubMenuTitleEl_.innerHTML = this.player_.localize(this.subMenu.controlText_)
     this.settingsSubMenuEl_.appendChild(this.subMenu.menu.el_)
     this.panelChildEl.appendChild(this.settingsSubMenuEl_)
     this.update()
index 487b3a1be055c9114d4a872dc4465c2126ab049b..ce7aaea2afa45ec8f0d623f50130810c73f4ceb8 100644 (file)
@@ -1,3 +1,5 @@
+import { is18nLocale, isDefaultLocale } from '../../../../shared/models/i18n/i18n'
+
 function toTitleCase (str: string) {
   return str.charAt(0).toUpperCase() + str.slice(1)
 }
index baeb22b6455a3ea052969c17842eef1217da7c95..10945c66557835e5f00211093a9e52bef1224337 100644 (file)
@@ -60,13 +60,8 @@ class WebtorrentInfoButton extends Button {
       className: 'peers-number',
       textContent: 'HTTP'
     })
-    const subDivFallbackText = videojsUntyped.dom.createEl('span', {
-      className: 'peers-text',
-      textContent: ' fallback'
-    })
 
     subDivHttp.appendChild(subDivHttpText)
-    subDivHttp.appendChild(subDivFallbackText)
     div.appendChild(subDivHttp)
 
     this.player_.peertube().on('torrentInfo', (event, data) => {
@@ -89,7 +84,7 @@ class WebtorrentInfoButton extends Button {
       uploadSpeedUnit.textContent = ' ' + uploadSpeed[ 1 ]
 
       peersNumber.textContent = numPeers
-      peersText.textContent = ' peers'
+      peersText.textContent = ' ' + this.player_.localize('peers')
 
       subDivHttp.className = 'vjs-peertube-hidden'
       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 (file)
index 0000000..5bb6afd
--- /dev/null
@@ -0,0 +1,378 @@
+<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">
+  <file original="namespace1" datatype="plaintext" source-language="undefined" target-language="undefined">
+    <body>
+      <trans-unit id="Audio Player">
+        <source>Audio Player</source>
+        <target>undefined</target>
+      </trans-unit>
+      <trans-unit id="Video Player">
+        <source>Video Player</source>
+        <target>undefined</target>
+      </trans-unit>
+      <trans-unit id="Play">
+        <source>Play</source>
+        <target>undefined</target>
+      </trans-unit>
+      <trans-unit id="Pause">
+        <source>Pause</source>
+        <target>undefined</target>
+      </trans-unit>
+      <trans-unit id="Replay">
+        <source>Replay</source>
+        <target>undefined</target>
+      </trans-unit>
+      <trans-unit id="Current Time">
+        <source>Current Time</source>
+        <target>undefined</target>
+      </trans-unit>
+      <trans-unit id="Duration">
+        <source>Duration</source>
+        <target>undefined</target>
+      </trans-unit>
+      <trans-unit id="Remaining Time">
+        <source>Remaining Time</source>
+        <target>undefined</target>
+      </trans-unit>
+      <trans-unit id="Stream Type">
+        <source>Stream Type</source>
+        <target>undefined</target>
+      </trans-unit>
+      <trans-unit id="LIVE">
+        <source>LIVE</source>
+        <target>undefined</target>
+      </trans-unit>
+      <trans-unit id="Loaded">
+        <source>Loaded</source>
+        <target>undefined</target>
+      </trans-unit>
+      <trans-unit id="Progress">
+        <source>Progress</source>
+        <target>undefined</target>
+      </trans-unit>
+      <trans-unit id="Progress Bar">
+        <source>Progress Bar</source>
+        <target>undefined</target>
+      </trans-unit>
+      <trans-unit id="progress bar timing: currentTime={1} duration={2}">
+        <source>{1} of {2}</source>
+        <target>undefined</target>
+      </trans-unit>
+      <trans-unit id="Fullscreen">
+        <source>Fullscreen</source>
+        <target>undefined</target>
+      </trans-unit>
+      <trans-unit id="Non-Fullscreen">
+        <source>Non-Fullscreen</source>
+        <target>undefined</target>
+      </trans-unit>
+      <trans-unit id="Mute">
+        <source>Mute</source>
+        <target>undefined</target>
+      </trans-unit>
+      <trans-unit id="Unmute">
+        <source>Unmute</source>
+        <target>undefined</target>
+      </trans-unit>
+      <trans-unit id="Playback Rate">
+        <source>Playback Rate</source>
+        <target>undefined</target>
+      </trans-unit>
+      <trans-unit id="Subtitles">
+        <source>Subtitles</source>
+        <target>undefined</target>
+      </trans-unit>
+      <trans-unit id="subtitles off">
+        <source>subtitles off</source>
+        <target>undefined</target>
+      </trans-unit>
+      <trans-unit id="Captions">
+        <source>Captions</source>
+        <target>undefined</target>
+      </trans-unit>
+      <trans-unit id="captions off">
+        <source>captions off</source>
+        <target>undefined</target>
+      </trans-unit>
+      <trans-unit id="Chapters">
+        <source>Chapters</source>
+        <target>undefined</target>
+      </trans-unit>
+      <trans-unit id="Descriptions">
+        <source>Descriptions</source>
+        <target>undefined</target>
+      </trans-unit>
+      <trans-unit id="descriptions off">
+        <source>descriptions off</source>
+        <target>undefined</target>
+      </trans-unit>
+      <trans-unit id="Audio Track">
+        <source>Audio Track</source>
+        <target>undefined</target>
+      </trans-unit>
+      <trans-unit id="Volume Level">
+        <source>Volume Level</source>
+        <target>undefined</target>
+      </trans-unit>
+      <trans-unit id="You aborted the media playback">
+        <source>You aborted the media playback</source>
+        <target>undefined</target>
+      </trans-unit>
+      <trans-unit id="A network error caused the media download to fail part-way.">
+        <source>A network error caused the media download to fail part-way.</source>
+        <target>undefined</target>
+      </trans-unit>
+      <trans-unit id="The media could not be loaded, either because the server or network failed or because the format is not supported.">
+        <source>The media could not be loaded, either because the server or network failed or because the format is not supported.</source>
+        <target>undefined</target>
+      </trans-unit>
+      <trans-unit id="The media playback was aborted due to a corruption problem or because the media used features your browser did not support.">
+        <source>The media playback was aborted due to a corruption problem or because the media used features your browser did not support.</source>
+        <target>undefined</target>
+      </trans-unit>
+      <trans-unit id="No compatible source was found for this media.">
+        <source>No compatible source was found for this media.</source>
+        <target>undefined</target>
+      </trans-unit>
+      <trans-unit id="The media is encrypted and we do not have the keys to decrypt it.">
+        <source>The media is encrypted and we do not have the keys to decrypt it.</source>
+        <target>undefined</target>
+      </trans-unit>
+      <trans-unit id="Play Video">
+        <source>Play Video</source>
+        <target>undefined</target>
+      </trans-unit>
+      <trans-unit id="Close">
+        <source>Close</source>
+        <target>undefined</target>
+      </trans-unit>
+      <trans-unit id="Close Modal Dialog">
+        <source>Close Modal Dialog</source>
+        <target>undefined</target>
+      </trans-unit>
+      <trans-unit id="Modal Window">
+        <source>Modal Window</source>
+        <target>undefined</target>
+      </trans-unit>
+      <trans-unit id="This is a modal window">
+        <source>This is a modal window</source>
+        <target>undefined</target>
+      </trans-unit>
+      <trans-unit id="This modal can be closed by pressing the Escape key or activating the close button.">
+        <source>This modal can be closed by pressing the Escape key or activating the close button.</source>
+        <target>undefined</target>
+      </trans-unit>
+      <trans-unit id=", opens captions settings dialog">
+        <source>, opens captions settings dialog</source>
+        <target>undefined</target>
+      </trans-unit>
+      <trans-unit id=", opens subtitles settings dialog">
+        <source>, opens subtitles settings dialog</source>
+        <target>undefined</target>
+      </trans-unit>
+      <trans-unit id=", opens descriptions settings dialog">
+        <source>, opens descriptions settings dialog</source>
+        <target>undefined</target>
+      </trans-unit>
+      <trans-unit id=", selected">
+        <source>, selected</source>
+        <target>undefined</target>
+      </trans-unit>
+      <trans-unit id="captions settings">
+        <source>captions settings</source>
+        <target>undefined</target>
+      </trans-unit>
+      <trans-unit id="subtitles settings">
+        <source>subititles settings</source>
+        <target>undefined</target>
+      </trans-unit>
+      <trans-unit id="descriptions settings">
+        <source>descriptions settings</source>
+        <target>undefined</target>
+      </trans-unit>
+      <trans-unit id="Text">
+        <source>Text</source>
+        <target>undefined</target>
+      </trans-unit>
+      <trans-unit id="White">
+        <source>White</source>
+        <target>undefined</target>
+      </trans-unit>
+      <trans-unit id="Black">
+        <source>Black</source>
+        <target>undefined</target>
+      </trans-unit>
+      <trans-unit id="Red">
+        <source>Red</source>
+        <target>undefined</target>
+      </trans-unit>
+      <trans-unit id="Green">
+        <source>Green</source>
+        <target>undefined</target>
+      </trans-unit>
+      <trans-unit id="Blue">
+        <source>Blue</source>
+        <target>undefined</target>
+      </trans-unit>
+      <trans-unit id="Yellow">
+        <source>Yellow</source>
+        <target>undefined</target>
+      </trans-unit>
+      <trans-unit id="Magenta">
+        <source>Magenta</source>
+        <target>undefined</target>
+      </trans-unit>
+      <trans-unit id="Cyan">
+        <source>Cyan</source>
+        <target>undefined</target>
+      </trans-unit>
+      <trans-unit id="Background">
+        <source>Background</source>
+        <target>undefined</target>
+      </trans-unit>
+      <trans-unit id="Window">
+        <source>Window</source>
+        <target>undefined</target>
+      </trans-unit>
+      <trans-unit id="Transparent">
+        <source>Transparent</source>
+        <target>undefined</target>
+      </trans-unit>
+      <trans-unit id="Semi-Transparent">
+        <source>Semi-Transparent</source>
+        <target>undefined</target>
+      </trans-unit>
+      <trans-unit id="Opaque">
+        <source>Opaque</source>
+        <target>undefined</target>
+      </trans-unit>
+      <trans-unit id="Font Size">
+        <source>Font Size</source>
+        <target>undefined</target>
+      </trans-unit>
+      <trans-unit id="Text Edge Style">
+        <source>Text Edge Style</source>
+        <target>undefined</target>
+      </trans-unit>
+      <trans-unit id="None">
+        <source>None</source>
+        <target>undefined</target>
+      </trans-unit>
+      <trans-unit id="Raised">
+        <source>Raised</source>
+        <target>undefined</target>
+      </trans-unit>
+      <trans-unit id="Depressed">
+        <source>Depressed</source>
+        <target>undefined</target>
+      </trans-unit>
+      <trans-unit id="Uniform">
+        <source>Uniform</source>
+        <target>undefined</target>
+      </trans-unit>
+      <trans-unit id="Dropshadow">
+        <source>Dropshadow</source>
+        <target>undefined</target>
+      </trans-unit>
+      <trans-unit id="Font Family">
+        <source>Font Family</source>
+        <target>undefined</target>
+      </trans-unit>
+      <trans-unit id="Proportional Sans-Serif">
+        <source>Proportional Sans-Serif</source>
+        <target>undefined</target>
+      </trans-unit>
+      <trans-unit id="Monospace Sans-Serif">
+        <source>Monospace Sans-Serif</source>
+        <target>undefined</target>
+      </trans-unit>
+      <trans-unit id="Proportional Serif">
+        <source>Proportional Serif</source>
+        <target>undefined</target>
+      </trans-unit>
+      <trans-unit id="Monospace Serif">
+        <source>Monospace Serif</source>
+        <target>undefined</target>
+      </trans-unit>
+      <trans-unit id="Casual">
+        <source>Casual</source>
+        <target>undefined</target>
+      </trans-unit>
+      <trans-unit id="Script">
+        <source>Script</source>
+        <target>undefined</target>
+      </trans-unit>
+      <trans-unit id="Small Caps">
+        <source>Small Caps</source>
+        <target>undefined</target>
+      </trans-unit>
+      <trans-unit id="Reset">
+        <source>Reset</source>
+        <target>undefined</target>
+      </trans-unit>
+      <trans-unit id="restore all settings to the default values">
+        <source>restore all settings to the default values</source>
+        <target>undefined</target>
+      </trans-unit>
+      <trans-unit id="Done">
+        <source>Done</source>
+        <target>undefined</target>
+      </trans-unit>
+      <trans-unit id="Caption Settings Dialog">
+        <source>Caption Settings Dialog</source>
+        <target>undefined</target>
+      </trans-unit>
+      <trans-unit id="Beginning of dialog window. Escape will cancel and close the window.">
+        <source>Beginning of dialog window. Escape will cancel and close the window.</source>
+        <target>undefined</target>
+      </trans-unit>
+      <trans-unit id="End of dialog window.">
+        <source>End of dialog window.</source>
+        <target>undefined</target>
+      </trans-unit>
+      <trans-unit id="{1} is loading.">
+        <source>{1} is loading.</source>
+        <target>undefined</target>
+      </trans-unit>
+      <trans-unit id="Quality">
+        <source>Quality</source>
+        <target>undefined</target>
+      </trans-unit>
+      <trans-unit id="Auto">
+        <source>Auto</source>
+        <target>undefined</target>
+      </trans-unit>
+      <trans-unit id="Speed">
+        <source>Speed</source>
+        <target>undefined</target>
+      </trans-unit>
+      <trans-unit id="peers">
+        <source>peers</source>
+        <target>undefined</target>
+      </trans-unit>
+      <trans-unit id="Go to the video page">
+        <source>Go to the video page</source>
+        <target>undefined</target>
+      </trans-unit>
+      <trans-unit id="Settings">
+        <source>Settings</source>
+        <target>undefined</target>
+      </trans-unit>
+      <trans-unit id="Uses P2P, others may know you are watching this video.">
+        <source>Uses P2P, others may know you are watching this video.</source>
+        <target>undefined</target>
+      </trans-unit>
+      <trans-unit id="Copy the video URL">
+        <source>Copy the video URL</source>
+        <target>undefined</target>
+      </trans-unit>
+      <trans-unit id="Copy the video URL at the current time">
+        <source>Copy the video URL at the current time</source>
+        <target>undefined</target>
+      </trans-unit>
+      <trans-unit id="Copy embed code">
+        <source>Copy embed code</source>
+        <target>undefined</target>
+      </trans-unit>
+    </body>
+  </file>
+</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 (file)
index 0000000..92caaa6
--- /dev/null
@@ -0,0 +1,85 @@
+{
+ "Audio Player": "Audio Player",
+ "Video Player": "Video Player",
+ "Play": "Play",
+ "Pause": "Pause",
+ "Replay": "Replay",
+ "Current Time": "Current Time",
+ "Duration": "Duration",
+ "Remaining Time": "Remaining Time",
+ "Stream Type": "Stream Type",
+ "LIVE": "LIVE",
+ "Loaded": "Loaded",
+ "Progress": "Progress",
+ "Progress Bar": "Progress Bar",
+ "progress bar timing: currentTime={1} duration={2}": "{1} of {2}",
+ "Fullscreen": "Fullscreen",
+ "Non-Fullscreen": "Non-Fullscreen",
+ "Mute": "Mute",
+ "Unmute": "Unmute",
+ "Playback Rate": "Playback Rate",
+ "Subtitles": "Subtitles",
+ "subtitles off": "subtitles off",
+ "Captions": "Captions",
+ "captions off": "captions off",
+ "Chapters": "Chapters",
+ "Descriptions": "Descriptions",
+ "descriptions off": "descriptions off",
+ "Audio Track": "Audio Track",
+ "Volume Level": "Volume Level",
+ "You aborted the media playback": "You aborted the media playback",
+ "A network error caused the media download to fail part-way.": "A network error caused the media download to fail part-way.",
+ "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.",
+ "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.",
+ "No compatible source was found for this media.": "No compatible source was found for this media.",
+ "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.",
+ "Play Video": "Play Video",
+ "Close": "Close",
+ "Close Modal Dialog": "Close Modal Dialog",
+ "Modal Window": "Modal Window",
+ "This is a modal window": "This is a modal window",
+ "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.",
+ ", opens captions settings dialog": ", opens captions settings dialog",
+ ", opens subtitles settings dialog": ", opens subtitles settings dialog",
+ ", opens descriptions settings dialog": ", opens descriptions settings dialog",
+ ", selected": ", selected",
+ "captions settings": "captions settings",
+ "subtitles settings": "subititles settings",
+ "descriptions settings": "descriptions settings",
+ "Text": "Text",
+ "White": "White",
+ "Black": "Black",
+ "Red": "Red",
+ "Green": "Green",
+ "Blue": "Blue",
+ "Yellow": "Yellow",
+ "Magenta": "Magenta",
+ "Cyan": "Cyan",
+ "Background": "Background",
+ "Window": "Window",
+ "Transparent": "Transparent",
+ "Semi-Transparent": "Semi-Transparent",
+ "Opaque": "Opaque",
+ "Font Size": "Font Size",
+ "Text Edge Style": "Text Edge Style",
+ "None": "None",
+ "Raised": "Raised",
+ "Depressed": "Depressed",
+ "Uniform": "Uniform",
+ "Dropshadow": "Dropshadow",
+ "Font Family": "Font Family",
+ "Proportional Sans-Serif": "Proportional Sans-Serif",
+ "Monospace Sans-Serif": "Monospace Sans-Serif",
+ "Proportional Serif": "Proportional Serif",
+ "Monospace Serif": "Monospace Serif",
+ "Casual": "Casual",
+ "Script": "Script",
+ "Small Caps": "Small Caps",
+ "Reset": "Reset",
+ "restore all settings to the default values": "restore all settings to the default values",
+ "Done": "Done",
+ "Caption Settings Dialog": "Caption Settings Dialog",
+ "Beginning of dialog window. Escape will cancel and close the window.": "Beginning of dialog window. Escape will cancel and close the window.",
+ "End of dialog window.": "End of dialog window.",
+ "{1} is loading.": "{1} is loading."
+}
diff --git a/client/src/locale/target/player_fr.json b/client/src/locale/target/player_fr.json
new file mode 100644 (file)
index 0000000..6c399fc
--- /dev/null
@@ -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 (file)
index 0000000..eafa4ba
--- /dev/null
@@ -0,0 +1,379 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--XLIFF document generated by Zanata. Visit http://zanata.org for more infomation.-->
+<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">
+  <file source-language="en-US" datatype="plaintext" original="" target-language="fr">
+    <body>
+      <trans-unit id="Audio Player">
+        <source>Audio Player</source>
+        <target>Lecteur audio</target>
+      </trans-unit>
+      <trans-unit id="Video Player">
+        <source>Video Player</source>
+        <target>Lecteur vidéo</target>
+      </trans-unit>
+      <trans-unit id="Play">
+        <source>Play</source>
+        <target>Lecture</target>
+      </trans-unit>
+      <trans-unit id="Pause">
+        <source>Pause</source>
+        <target>Pause</target>
+      </trans-unit>
+      <trans-unit id="Replay">
+        <source>Replay</source>
+        <target>Revoir</target>
+      </trans-unit>
+      <trans-unit id="Current Time">
+        <source>Current Time</source>
+        <target>Temps actuel</target>
+      </trans-unit>
+      <trans-unit id="Duration">
+        <source>Duration</source>
+        <target>Durée</target>
+      </trans-unit>
+      <trans-unit id="Remaining Time">
+        <source>Remaining Time</source>
+        <target>Temps restant</target>
+      </trans-unit>
+      <trans-unit id="Stream Type">
+        <source>Stream Type</source>
+        <target>Type de flux</target>
+      </trans-unit>
+      <trans-unit id="LIVE">
+        <source>LIVE</source>
+        <target>EN DIRECT</target>
+      </trans-unit>
+      <trans-unit id="Loaded">
+        <source>Loaded</source>
+        <target>Chargé</target>
+      </trans-unit>
+      <trans-unit id="Progress">
+        <source>Progress</source>
+        <target>Progression</target>
+      </trans-unit>
+      <trans-unit id="Progress Bar">
+        <source>Progress Bar</source>
+        <target>Barre de progression</target>
+      </trans-unit>
+      <trans-unit id="progress bar timing: currentTime={1} duration={2}">
+        <source>{1} of {2}</source>
+        <target>{1} de {2}</target>
+      </trans-unit>
+      <trans-unit id="Fullscreen">
+        <source>Fullscreen</source>
+        <target>Plein écran</target>
+      </trans-unit>
+      <trans-unit id="Non-Fullscreen">
+        <source>Non-Fullscreen</source>
+        <target>Fenêtré</target>
+      </trans-unit>
+      <trans-unit id="Mute">
+        <source>Mute</source>
+        <target>Sourdine</target>
+      </trans-unit>
+      <trans-unit id="Unmute">
+        <source>Unmute</source>
+        <target>Son activé</target>
+      </trans-unit>
+      <trans-unit id="Playback Rate">
+        <source>Playback Rate</source>
+        <target>Vitesse de lecture</target>
+      </trans-unit>
+      <trans-unit id="Subtitles">
+        <source>Subtitles</source>
+        <target>Sous-titres</target>
+      </trans-unit>
+      <trans-unit id="subtitles off">
+        <source>subtitles off</source>
+        <target>Sous-titres désactivés</target>
+      </trans-unit>
+      <trans-unit id="Captions">
+        <source>Captions</source>
+        <target>Sous-titres transcrits</target>
+      </trans-unit>
+      <trans-unit id="captions off">
+        <source>captions off</source>
+        <target>Sous-titres transcrits désactivés</target>
+      </trans-unit>
+      <trans-unit id="Chapters">
+        <source>Chapters</source>
+        <target>Chapitres</target>
+      </trans-unit>
+      <trans-unit id="Descriptions">
+        <source>Descriptions</source>
+        <target>Descriptions</target>
+      </trans-unit>
+      <trans-unit id="descriptions off">
+        <source>descriptions off</source>
+        <target>descriptions désactivées</target>
+      </trans-unit>
+      <trans-unit id="Audio Track">
+        <source>Audio Track</source>
+        <target>Piste audio</target>
+      </trans-unit>
+      <trans-unit id="Volume Level">
+        <source>Volume Level</source>
+        <target>Niveau de volume</target>
+      </trans-unit>
+      <trans-unit id="You aborted the media playback">
+        <source>You aborted the media playback</source>
+        <target>Vous avez interrompu la lecture de la vidéo.</target>
+      </trans-unit>
+      <trans-unit id="A network error caused the media download to fail part-way.">
+        <source>A network error caused the media download to fail part-way.</source>
+        <target>Une erreur de réseau a interrompu le téléchargement de la vidéo.</target>
+      </trans-unit>
+      <trans-unit id="The media could not be loaded, either because the server or network failed or because the format is not supported.">
+        <source>The media could not be loaded, either because the server or network failed or because the format is not supported.</source>
+        <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>
+      </trans-unit>
+      <trans-unit id="The media playback was aborted due to a corruption problem or because the media used features your browser did not support.">
+        <source>The media playback was aborted due to a corruption problem or because the media used features your browser did not support.</source>
+        <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>
+      </trans-unit>
+      <trans-unit id="No compatible source was found for this media.">
+        <source>No compatible source was found for this media.</source>
+        <target>Aucune source compatible n'a été trouvée pour cette vidéo.</target>
+      </trans-unit>
+      <trans-unit id="The media is encrypted and we do not have the keys to decrypt it.">
+        <source>The media is encrypted and we do not have the keys to decrypt it.</source>
+        <target>Le média est chiffré et nous n'avons pas les clés pour le déchiffrer.</target>
+      </trans-unit>
+      <trans-unit id="Play Video">
+        <source>Play Video</source>
+        <target>Lire la vidéo</target>
+      </trans-unit>
+      <trans-unit id="Close">
+        <source>Close</source>
+        <target>Fermer</target>
+      </trans-unit>
+      <trans-unit id="Close Modal Dialog">
+        <source>Close Modal Dialog</source>
+        <target>Fermer la boîte de dialogue modale</target>
+      </trans-unit>
+      <trans-unit id="Modal Window">
+        <source>Modal Window</source>
+        <target>Fenêtre modale</target>
+      </trans-unit>
+      <trans-unit id="This is a modal window">
+        <source>This is a modal window</source>
+        <target>Ceci est une fenêtre modale</target>
+      </trans-unit>
+      <trans-unit id="This modal can be closed by pressing the Escape key or activating the close button.">
+        <source>This modal can be closed by pressing the Escape key or activating the close button.</source>
+        <target>Ce modal peut être fermé en appuyant sur la touche Échap ou activer le bouton de fermeture.</target>
+      </trans-unit>
+      <trans-unit id=", opens captions settings dialog">
+        <source>, opens captions settings dialog</source>
+        <target>, ouvrir les paramètres des sous-titres transcrits</target>
+      </trans-unit>
+      <trans-unit id=", opens subtitles settings dialog">
+        <source>, opens subtitles settings dialog</source>
+        <target>, ouvrir les paramètres des sous-titres</target>
+      </trans-unit>
+      <trans-unit id=", opens descriptions settings dialog">
+        <source>, opens descriptions settings dialog</source>
+        <target>, ouvrir les paramètres des descriptions</target>
+      </trans-unit>
+      <trans-unit id=", selected">
+        <source>, selected</source>
+        <target>, sélectionné</target>
+      </trans-unit>
+      <trans-unit id="captions settings">
+        <source>captions settings</source>
+        <target>Paramètres des sous-titres transcrits</target>
+      </trans-unit>
+      <trans-unit id="subtitles settings">
+        <source>subititles settings</source>
+        <target>Paramètres des sous-titres</target>
+      </trans-unit>
+      <trans-unit id="descriptions settings">
+        <source>descriptions settings</source>
+        <target>Paramètres des descriptions</target>
+      </trans-unit>
+      <trans-unit id="Text">
+        <source>Text</source>
+        <target>Texte</target>
+      </trans-unit>
+      <trans-unit id="White">
+        <source>White</source>
+        <target>Blanc</target>
+      </trans-unit>
+      <trans-unit id="Black">
+        <source>Black</source>
+        <target>Noir</target>
+      </trans-unit>
+      <trans-unit id="Red">
+        <source>Red</source>
+        <target>Rouge</target>
+      </trans-unit>
+      <trans-unit id="Green">
+        <source>Green</source>
+        <target>Vert</target>
+      </trans-unit>
+      <trans-unit id="Blue">
+        <source>Blue</source>
+        <target>Bleu</target>
+      </trans-unit>
+      <trans-unit id="Yellow">
+        <source>Yellow</source>
+        <target>Jaune</target>
+      </trans-unit>
+      <trans-unit id="Magenta">
+        <source>Magenta</source>
+        <target>Magenta</target>
+      </trans-unit>
+      <trans-unit id="Cyan">
+        <source>Cyan</source>
+        <target>Cyan</target>
+      </trans-unit>
+      <trans-unit id="Background">
+        <source>Background</source>
+        <target>Arrière-plan</target>
+      </trans-unit>
+      <trans-unit id="Window">
+        <source>Window</source>
+        <target>Fenêtre</target>
+      </trans-unit>
+      <trans-unit id="Transparent">
+        <source>Transparent</source>
+        <target>Transparent</target>
+      </trans-unit>
+      <trans-unit id="Semi-Transparent">
+        <source>Semi-Transparent</source>
+        <target>Semi-transparent</target>
+      </trans-unit>
+      <trans-unit id="Opaque">
+        <source>Opaque</source>
+        <target>Opaque</target>
+      </trans-unit>
+      <trans-unit id="Font Size">
+        <source>Font Size</source>
+        <target>Taille des caractères</target>
+      </trans-unit>
+      <trans-unit id="Text Edge Style">
+        <source>Text Edge Style</source>
+        <target>Style des contours du texte</target>
+      </trans-unit>
+      <trans-unit id="None">
+        <source>None</source>
+        <target>Aucun</target>
+      </trans-unit>
+      <trans-unit id="Raised">
+        <source>Raised</source>
+        <target>Élevé</target>
+      </trans-unit>
+      <trans-unit id="Depressed">
+        <source>Depressed</source>
+        <target>Enfoncé</target>
+      </trans-unit>
+      <trans-unit id="Uniform">
+        <source>Uniform</source>
+        <target>Uniforme</target>
+      </trans-unit>
+      <trans-unit id="Dropshadow">
+        <source>Dropshadow</source>
+        <target>Ombre portée</target>
+      </trans-unit>
+      <trans-unit id="Font Family">
+        <source>Font Family</source>
+        <target>Familles de polices</target>
+      </trans-unit>
+      <trans-unit id="Proportional Sans-Serif">
+        <source>Proportional Sans-Serif</source>
+        <target>Polices à chasse variable sans empattement (Proportional Sans-Serif)</target>
+      </trans-unit>
+      <trans-unit id="Monospace Sans-Serif">
+        <source>Monospace Sans-Serif</source>
+        <target>Polices à chasse fixe sans empattement (Monospace Sans-Serif)</target>
+      </trans-unit>
+      <trans-unit id="Proportional Serif">
+        <source>Proportional Serif</source>
+        <target>Polices à chasse variable avec empattement (Proportional Serif)</target>
+      </trans-unit>
+      <trans-unit id="Monospace Serif">
+        <source>Monospace Serif</source>
+        <target>Polices à chasse fixe avec empattement (Monospace Serif)</target>
+      </trans-unit>
+      <trans-unit id="Casual">
+        <source>Casual</source>
+        <target>Manuscrite</target>
+      </trans-unit>
+      <trans-unit id="Script">
+        <source>Script</source>
+        <target>Scripte</target>
+      </trans-unit>
+      <trans-unit id="Small Caps">
+        <source>Small Caps</source>
+        <target>Petites capitales</target>
+      </trans-unit>
+      <trans-unit id="Reset">
+        <source>Reset</source>
+        <target>Réinitialiser</target>
+      </trans-unit>
+      <trans-unit id="restore all settings to the default values">
+        <source>restore all settings to the default values</source>
+        <target>Restaurer tous les paramètres aux valeurs par défaut</target>
+      </trans-unit>
+      <trans-unit id="Done">
+        <source>Done</source>
+        <target>Terminé</target>
+      </trans-unit>
+      <trans-unit id="Caption Settings Dialog">
+        <source>Caption Settings Dialog</source>
+        <target>Boîte de dialogue des paramètres des sous-titres transcrits</target>
+      </trans-unit>
+      <trans-unit id="Beginning of dialog window. Escape will cancel and close the window.">
+        <source>Beginning of dialog window. Escape will cancel and close the window.</source>
+        <target>Début de la fenêtre de dialogue. La touche d'échappement annulera et fermera la fenêtre.</target>
+      </trans-unit>
+      <trans-unit id="End of dialog window.">
+        <source>End of dialog window.</source>
+        <target>Fin de la fenêtre de dialogue.</target>
+      </trans-unit>
+      <trans-unit id="{1} is loading.">
+        <source>{1} is loading.</source>
+        <target>{1} est en train de charger</target>
+      </trans-unit>
+      <trans-unit id="Quality">
+        <source>Quality</source>
+        <target>Qualité</target>
+      </trans-unit>
+      <trans-unit id="Auto">
+        <source>Auto</source>
+        <target>Auto</target>
+      </trans-unit>
+      <trans-unit id="Speed">
+        <source>Speed</source>
+        <target>Vitesse</target>
+      </trans-unit>
+      <trans-unit id="peers">
+        <source>peers</source>
+        <target>pairs</target>
+      </trans-unit>
+      <trans-unit id="Go to the video page">
+        <source>Go to the video page</source>
+        <target>Aller sur la page de la vidéo</target>
+      </trans-unit>
+      <trans-unit id="Settings">
+        <source>Settings</source>
+        <target>Paramètres</target>
+      </trans-unit>
+      <trans-unit id="Uses P2P, others may know you are watching this video.">
+        <source>Uses P2P, others may know you are watching this video.</source>
+        <target>Utilise le P2P, d'autres personnes pourraient savoir que vous regardez cette vidéo.</target>
+      </trans-unit>
+      <trans-unit id="Copy the video URL">
+        <source>Copy the video URL</source>
+        <target>Copier le lien de la vidéo</target>
+      </trans-unit>
+      <trans-unit id="Copy the video URL at the current time">
+        <source>Copy the video URL at the current time</source>
+        <target>Copier le lien de la vidéo à partir de cette séquence</target>
+      </trans-unit>
+      <trans-unit id="Copy embed code">
+        <source>Copy embed code</source>
+        <target>Copier le code d'intégration</target>
+      </trans-unit>
+    </body>
+  </file></xliff>
\ No newline at end of file
index d603690cacdfd89dc0265e4050496b19af018033..1660132264f86c91a3db2b90d35eeb26f0594f7b 100644 (file)
@@ -14,14 +14,14 @@ import 'core-js/es6/regexp'
 import 'core-js/es6/map'
 import 'core-js/es6/weak-map'
 import 'core-js/es6/set'
-
 // For google bot that uses Chrome 41 and does not understand fetch
 import 'whatwg-fetch'
 
 import * as videojs from 'video.js'
 
 import { VideoDetails } from '../../../../shared'
-import { getVideojsOptions } from '../../assets/player/peertube-player'
+import { addContextMenu, getVideojsOptions, loadLocale } from '../../assets/player/peertube-player'
+import { environment } from '../../environments/environment'
 
 function getVideoUrl (id: string) {
   return window.location.origin + '/api/v1/videos/' + id
@@ -61,7 +61,8 @@ function videoFetchError (videoElement: HTMLVideoElement) {
 const urlParts = window.location.href.split('/')
 const videoId = urlParts[urlParts.length - 1]
 
-loadVideoInfo(videoId)
+loadLocale(environment.apiUrl, videojs, navigator.language)
+  .then(() => loadVideoInfo(videoId))
   .then(async response => {
     const videoContainerId = 'video-container'
     const videoElement = document.getElementById(videoContainerId) as HTMLVideoElement
@@ -91,7 +92,6 @@ loadVideoInfo(videoId)
     const videojsOptions = getVideojsOptions({
       autoplay,
       inactivityTimeout: 1500,
-      videoEmbedUrl: window.location.origin + videoInfo.embedPath,
       videoViewUrl: getVideoUrl(videoId) + '/views',
       playerElement: videoElement,
       videoFiles: videoInfo.files,
@@ -106,8 +106,10 @@ loadVideoInfo(videoId)
 
       player.dock({
         title: videoInfo.name,
-        description: 'Uses P2P, others may know you are watching this video.'
+        description: player.localize('Uses P2P, others may know you are watching this video.')
       })
+
+      addContextMenu(player, window.location.origin + videoInfo.embedPath)
     })
   })
   .catch(err => console.error(err))
index 21701e6649b225c8afc8f9cf5fe33fd90f959498..707579af3c2184d256b60db8497ea5505bc7f9e4 100644 (file)
@@ -30,6 +30,8 @@
     "danger:clean:prod": "scripty",
     "danger:clean:modules": "scripty",
     "i18n:generate": "scripty",
+    "i18n:xliff2json": "node ./dist/scripts/i18n/xliff2json.js",
+    "i18n:create-custom-files": "node ./dist/scripts/i18n/create-custom-files.js",
     "reset-password": "node ./dist/scripts/reset-password.js",
     "play": "scripty",
     "dev": "scripty",
     "tslint-config-standard": "^7.0.0",
     "typescript": "^2.5.2",
     "webtorrent": "^0.100.0",
+    "xliff": "^3.0.1",
     "youtube-dl": "^1.12.2"
   },
   "scripty": {
diff --git a/scripts/i18n/create-custom-files.ts b/scripts/i18n/create-custom-files.ts
new file mode 100755 (executable)
index 0000000..3895b3b
--- /dev/null
@@ -0,0 +1,49 @@
+import * as jsToXliff12 from 'xliff/jsToXliff12'
+import { writeFile } from 'fs'
+import { join } from 'path'
+
+// First, the player
+const playerSource = join(__dirname, '../../../client/src/locale/source/videojs_en_US.json')
+const playerTarget = join(__dirname, '../../../client/src/locale/source/player_en_US.xml')
+
+const videojs = require(playerSource)
+const playerKeys = {
+  'Quality': 'Quality',
+  'Auto': 'Auto',
+  'Speed': 'Speed',
+  'peers': 'peers',
+  'Go to the video page': 'Go to the video page',
+  'Settings': 'Settings',
+  'Uses P2P, others may know you are watching this video.': 'Uses P2P, others may know you are watching this video.',
+  'Copy the video URL': 'Copy the video URL',
+  'Copy the video URL at the current time': 'Copy the video URL at the current time',
+  'Copy embed code': 'Copy embed code'
+}
+
+const obj = {
+  resources: {
+    namespace1: {}
+  }
+}
+
+for (const sourceObject of [ videojs, playerKeys ]) {
+  Object.keys(sourceObject).forEach(k => obj.resources.namespace1[ k ] = { source: sourceObject[ k ] })
+}
+
+jsToXliff12(obj, (err, res) => {
+  if (err) {
+    console.error(err)
+    process.exit(-1)
+  }
+
+  writeFile(playerTarget, res, err => {
+    if (err) {
+      console.error(err)
+      process.exit(-1)
+    }
+
+    process.exit(0)
+  })
+})
+
+// Then, the server strings
index f8ad8a3c15a5419be7c23d9e6c2ef8bc5ad55609..0a5b6dee160b8aca04580e9dc3abf9eb40e51f22 100755 (executable)
@@ -9,4 +9,8 @@ npm run ngx-extractor -- --locale "en-US" -i 'src/**/*.ts' -f xlf -o src/locale/
 # Zanata does not support inner elements in <source>, so we hack these special elements
 # This regex translate the Angular elements to special entities (that we will reconvert on pull)
 #sed -i 's/<x id=\(.\+\?\)\/>/\&lt;x id=\1\/\&gt;/g' src/locale/source/messages_en_US.xml
-perl -pi -e 's|<x id=(.+?)/>|&lt;x id=\1/&gt;|g' src/locale/source/messages_en_US.xml
\ No newline at end of file
+perl -pi -e 's|<x id=(.+?)/>|&lt;x id=\1/&gt;|g' src/locale/source/messages_en_US.xml
+
+# Add our strings too
+cd ../
+npm run i18n:create-custom-files
\ No newline at end of file
index bbe4a813eb992902e0f5244777805cb17379ee48..dec426b88e98a624ac1d463dc073006b738a2bb6 100755 (executable)
@@ -7,5 +7,7 @@ set -eu
 #sed -i 's/\&lt;x id=\(.\+\?\)\/\&gt;/<x id=\1\/>/g' client/src/locale/target/*
 
 for i in 1 2 3; do
-    perl -pi -e 's|&lt;x id=(.+?)/&gt;([^"])|<x id=\1/>\2|g' client/src/locale/target/*
-done
\ No newline at end of file
+    perl -pi -e 's|&lt;x id=(.+?)/&gt;([^"])|<x id=\1/>\2|g' client/src/locale/target/*.xml
+done
+
+npm 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 (executable)
index 0000000..34784ac
--- /dev/null
@@ -0,0 +1,42 @@
+import * as xliff12ToJs from 'xliff/xliff12ToJs'
+import { readFileSync, writeFile } from 'fs'
+import { join } from 'path'
+
+// First, the player
+const playerSource = join(__dirname, '../../../client/src/locale/target/player_fr.xml')
+const playerTarget = join(__dirname, '../../../client/src/locale/target/player_fr.json')
+
+// Remove the two first lines our xliff module does not like
+let playerFile = readFileSync(playerSource).toString()
+playerFile = removeFirstLine(playerFile)
+playerFile = removeFirstLine(playerFile)
+
+xliff12ToJs(playerFile, (err, res) => {
+  if (err) {
+    console.error(err)
+    process.exit(-1)
+  }
+
+  const json = createJSONString(res)
+  writeFile(playerTarget, json, err => {
+    if (err) {
+      console.error(err)
+      process.exit(-1)
+    }
+
+    process.exit(0)
+  })
+})
+
+function removeFirstLine (str: string) {
+  return str.substring(str.indexOf('\n') + 1)
+}
+
+function createJSONString (obj: any) {
+  const res: any = {}
+  const strings = obj.resources['']
+
+  Object.keys(strings).forEach(k => res[k] = strings[k].target)
+
+  return JSON.stringify(res)
+}
index 6250fb9a423e4ce6d6936371a43191131f08f487..badbf3da0dacf9c11a1212c47c3732b11cffe688 100755 (executable)
@@ -2,6 +2,11 @@
 
 set -eu
 
+# Copy locales
+mkdir -p "./client/dist"
+rm -r "./client/dist/locale"
+cp -r "./client/src/locale/target" "./client/dist/locale"
+
 NODE_ENV=test concurrently -k \
   "npm run tsc -- --sourceMap && npm run nodemon -- --delay 2 --watch ./dist dist/server" \
   "npm run tsc -- --sourceMap --preserveWatchOutput -w"
index 4b37b5fa6ca9a1534127649563273c0eff6b9da9..b153f6086fc0fbecdbb366383cc2e2882b4ba53d 100644 (file)
@@ -47,6 +47,14 @@ for (const staticClientFile of staticClientFiles) {
 clientsRouter.use('/client', express.static(distPath, { maxAge: STATIC_MAX_AGE }))
 clientsRouter.use('/client/assets/images', express.static(assetsImagesPath, { maxAge: STATIC_MAX_AGE }))
 
+clientsRouter.use('/client/locales/:locale/:file.json', function (req, res) {
+  if (req.params.locale === 'fr' && req.params.file === 'player') {
+    return res.sendFile(join(__dirname, '../../../client/dist/locale/player_fr.json'))
+  }
+
+  return res.sendStatus(404)
+})
+
 // 404 for static files not found
 clientsRouter.use('/client/*', (req: express.Request, res: express.Response, next: express.NextFunction) => {
   res.sendStatus(404)
index 2d3a1d3e274fd65a61141a304d3eb92474f87c8d..4d50bc36ea73edca5e4130c1766ec08655bf01a5 100644 (file)
@@ -7,6 +7,10 @@ export function getDefaultLocale () {
   return 'en-US'
 }
 
+export function isDefaultLocale (locale: string) {
+  return locale === getDefaultLocale()
+}
+
 const possiblePaths = Object.keys(I18N_LOCALES).map(l => '/' + l)
 export function is18nPath (path: string) {
   return possiblePaths.indexOf(path) !== -1
index eb06faac058c48cbfcc5764ff34b795877168a53..3b6c7574eb52dfdf484c60ec56468586c6aa19ef 100644 (file)
--- a/yarn.lock
+++ b/yarn.lock
@@ -8402,6 +8402,18 @@ xhr2@^0.1.4:
   version "0.1.4"
   resolved "https://registry.yarnpkg.com/xhr2/-/xhr2-0.1.4.tgz#7f87658847716db5026323812f818cadab387a5f"
 
+xliff@^3.0.1:
+  version "3.0.1"
+  resolved "https://registry.yarnpkg.com/xliff/-/xliff-3.0.1.tgz#ea0f5840011727aecbddf111e5c26d8590dcca9b"
+  dependencies:
+    xml-js "1.6.2"
+
+xml-js@1.6.2:
+  version "1.6.2"
+  resolved "https://registry.yarnpkg.com/xml-js/-/xml-js-1.6.2.tgz#4c4cb8413998f73701a202a1b8b2f17c985a72c5"
+  dependencies:
+    sax "^1.2.4"
+
 xml-name-validator@^3.0.0:
   version "3.0.0"
   resolved "https://registry.yarnpkg.com/xml-name-validator/-/xml-name-validator-3.0.0.tgz#6ae73e06de4d8c6e47f9fb181f78d648ad457c6a"